changeset 10:b6c35faf97c8

Viewers
author Frank Benoit <benoit@tionex.de>
date Mon, 31 Mar 2008 00:47:19 +0200
parents 6c14e54dfc11
children 11bd25f93332
files dwtx/jface/layout/AbstractColumnLayout.d dwtx/jface/layout/TableColumnLayout.d dwtx/jface/util/SafeRunnable.d dwtx/jface/util/SafeRunnableDialog.d dwtx/jface/util/Util.d dwtx/jface/viewers/AbstractListViewer.d dwtx/jface/viewers/AbstractTableViewer.d dwtx/jface/viewers/AbstractTreeViewer.d dwtx/jface/viewers/AcceptAllFilter.d dwtx/jface/viewers/ArrayContentProvider.d dwtx/jface/viewers/BaseLabelProvider.d dwtx/jface/viewers/CellEditor.d dwtx/jface/viewers/CellLabelProvider.d dwtx/jface/viewers/CellNavigationStrategy.d dwtx/jface/viewers/CheckStateChangedEvent.d dwtx/jface/viewers/CheckboxCellEditor.d dwtx/jface/viewers/CheckboxTableViewer.d dwtx/jface/viewers/CheckboxTreeViewer.d dwtx/jface/viewers/ColorCellEditor.d dwtx/jface/viewers/ColumnLabelProvider.d dwtx/jface/viewers/ColumnLayoutData.d dwtx/jface/viewers/ColumnPixelData.d dwtx/jface/viewers/ColumnViewer.d dwtx/jface/viewers/ColumnViewerEditor.d dwtx/jface/viewers/ColumnViewerEditorActivationEvent.d dwtx/jface/viewers/ColumnViewerEditorActivationListener.d dwtx/jface/viewers/ColumnViewerEditorActivationStrategy.d dwtx/jface/viewers/ColumnViewerEditorDeactivationEvent.d dwtx/jface/viewers/ColumnViewerToolTipSupport.d dwtx/jface/viewers/ColumnWeightData.d dwtx/jface/viewers/ComboBoxCellEditor.d dwtx/jface/viewers/ComboViewer.d dwtx/jface/viewers/ContentViewer.d dwtx/jface/viewers/CustomHashtable.d dwtx/jface/viewers/DecoratingLabelProvider.d dwtx/jface/viewers/DecorationContext.d dwtx/jface/viewers/DecorationOverlayIcon.d dwtx/jface/viewers/DialogCellEditor.d dwtx/jface/viewers/DoubleClickEvent.d dwtx/jface/viewers/EditingSupport.d dwtx/jface/viewers/FocusCellHighlighter.d dwtx/jface/viewers/FocusCellOwnerDrawHighlighter.d dwtx/jface/viewers/IBaseLabelProvider.d dwtx/jface/viewers/IBasicPropertyConstants.d dwtx/jface/viewers/ICellEditorListener.d dwtx/jface/viewers/ICellEditorValidator.d dwtx/jface/viewers/ICellModifier.d dwtx/jface/viewers/ICheckStateListener.d dwtx/jface/viewers/ICheckable.d dwtx/jface/viewers/IColorDecorator.d dwtx/jface/viewers/IColorProvider.d dwtx/jface/viewers/IContentProvider.d dwtx/jface/viewers/IDecoration.d dwtx/jface/viewers/IDecorationContext.d dwtx/jface/viewers/IDelayedLabelDecorator.d dwtx/jface/viewers/IDoubleClickListener.d dwtx/jface/viewers/IElementComparer.d dwtx/jface/viewers/IFilter.d dwtx/jface/viewers/IFontDecorator.d dwtx/jface/viewers/IFontProvider.d dwtx/jface/viewers/IInputProvider.d dwtx/jface/viewers/IInputSelectionProvider.d dwtx/jface/viewers/ILabelDecorator.d dwtx/jface/viewers/ILabelProvider.d dwtx/jface/viewers/ILabelProviderListener.d dwtx/jface/viewers/ILazyContentProvider.d dwtx/jface/viewers/ILazyTreeContentProvider.d dwtx/jface/viewers/ILazyTreePathContentProvider.d dwtx/jface/viewers/ILightweightLabelDecorator.d dwtx/jface/viewers/IOpenListener.d dwtx/jface/viewers/IPostSelectionProvider.d dwtx/jface/viewers/ISelection.d dwtx/jface/viewers/ISelectionChangedListener.d dwtx/jface/viewers/ISelectionProvider.d dwtx/jface/viewers/IStructuredContentProvider.d dwtx/jface/viewers/IStructuredSelection.d dwtx/jface/viewers/ITableColorProvider.d dwtx/jface/viewers/ITableFontProvider.d dwtx/jface/viewers/ITableLabelProvider.d dwtx/jface/viewers/ITreeContentProvider.d dwtx/jface/viewers/ITreePathContentProvider.d dwtx/jface/viewers/ITreePathLabelProvider.d dwtx/jface/viewers/ITreeSelection.d dwtx/jface/viewers/ITreeViewerListener.d dwtx/jface/viewers/IViewerLabelProvider.d dwtx/jface/viewers/LabelDecorator.d dwtx/jface/viewers/LabelProvider.d dwtx/jface/viewers/LabelProviderChangedEvent.d dwtx/jface/viewers/ListViewer.d dwtx/jface/viewers/NamedHandleObjectLabelProvider.d dwtx/jface/viewers/OpenEvent.d dwtx/jface/viewers/OwnerDrawLabelProvider.d dwtx/jface/viewers/SWTFocusCellManager.d dwtx/jface/viewers/SelectionChangedEvent.d dwtx/jface/viewers/StructuredSelection.d dwtx/jface/viewers/StructuredViewer.d dwtx/jface/viewers/TableColumnViewerLabelProvider.d dwtx/jface/viewers/TableLayout.d dwtx/jface/viewers/TableTreeViewer.d dwtx/jface/viewers/TableViewer.d dwtx/jface/viewers/TableViewerColumn.d dwtx/jface/viewers/TableViewerEditor.d dwtx/jface/viewers/TableViewerFocusCellManager.d dwtx/jface/viewers/TableViewerRow.d dwtx/jface/viewers/TextCellEditor.d dwtx/jface/viewers/TreeColumnViewerLabelProvider.d dwtx/jface/viewers/TreeExpansionEvent.d dwtx/jface/viewers/TreeNode.d dwtx/jface/viewers/TreeNodeContentProvider.d dwtx/jface/viewers/TreePath.d dwtx/jface/viewers/TreePathViewerSorter.d dwtx/jface/viewers/TreeSelection.d dwtx/jface/viewers/TreeViewer.d dwtx/jface/viewers/TreeViewerColumn.d dwtx/jface/viewers/TreeViewerEditor.d dwtx/jface/viewers/TreeViewerFocusCellManager.d dwtx/jface/viewers/TreeViewerRow.d dwtx/jface/viewers/Viewer.d dwtx/jface/viewers/ViewerCell.d dwtx/jface/viewers/ViewerColumn.d dwtx/jface/viewers/ViewerComparator.d dwtx/jface/viewers/ViewerDropAdapter.d dwtx/jface/viewers/ViewerFilter.d dwtx/jface/viewers/ViewerLabel.d dwtx/jface/viewers/ViewerRow.d dwtx/jface/viewers/ViewerSorter.d dwtx/jface/viewers/WrappedViewerLabelProvider.d dwtx/jface/viewers/deferred/AbstractConcurrentModel.d dwtx/jface/viewers/deferred/AbstractVirtualTable.d dwtx/jface/viewers/deferred/BackgroundContentProvider.d dwtx/jface/viewers/deferred/ChangeQueue.d dwtx/jface/viewers/deferred/ConcurrentTableUpdator.d dwtx/jface/viewers/deferred/DeferredContentProvider.d dwtx/jface/viewers/deferred/FastProgressReporter.d dwtx/jface/viewers/deferred/IConcurrentModel.d dwtx/jface/viewers/deferred/IConcurrentModelListener.d dwtx/jface/viewers/deferred/IntHashMap.d dwtx/jface/viewers/deferred/LazySortedCollection.d dwtx/jface/viewers/deferred/SetModel.d dwtx/jface/viewers/deferred/package.html
diffstat 140 files changed, 32016 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/layout/AbstractColumnLayout.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,320 @@
+/*******************************************************************************
+ * 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 (original file dwtx.ui.texteditor.templates.ColumnLayout)
+ *     Tom Schindl <tom.schindl@bestsolution.at> - refactored to be widget independent (bug 171824)
+ *                                               - fix for bug 178280, 184342, 184045
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.layout.AbstractColumnLayout;
+
+
+
+import dwt.DWT;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Composite;
+import dwt.widgets.Event;
+import dwt.widgets.Layout;
+import dwt.widgets.Listener;
+import dwt.widgets.Scrollable;
+import dwt.widgets.Widget;
+import dwtx.core.runtime.Assert;
+import dwtx.jface.util.Policy;
+import dwtx.jface.viewers.ColumnLayoutData;
+import dwtx.jface.viewers.ColumnPixelData;
+import dwtx.jface.viewers.ColumnWeightData;
+import dwtx.jface.viewers.TableLayout;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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.3
+ */
+abstract class AbstractColumnLayout : 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;
+
+    static const bool IS_GTK;
+
+    static const String LAYOUT_DATA;
+
+    static this(){
+        COLUMN_TRIM = "carbon".equals(DWT.getPlatform()) ? 24 : 3; //$NON-NLS-1$
+        IS_GTK = "gtk".equals(DWT.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(DWT.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 += 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;
+    }
+
+    /**
+     * 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 size = getColumnCount(scrollable);
+        int[] widths = new int[size];
+
+        int[] weightIteration = new int[size];
+        int numberOfWeightColumns = 0;
+
+        int fixedWidth = 0;
+        int minWeightWidth = 0;
+        int totalWeight = 0;
+
+        // First calc space occupied by fixed columns
+        for (int i = 0; i < size; i++) {
+            ColumnLayoutData col = getLayoutData(scrollable,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 ) {
+                weightIteration[numberOfWeightColumns] = i;
+                numberOfWeightColumns++;
+                totalWeight += cw.weight;
+                minWeightWidth += cw.minimumWidth;
+                widths[i] = cw.minimumWidth;
+            } else {
+                Assert.isTrue(false, "Unknown column layout data"); //$NON-NLS-1$
+            }
+        }
+
+        // Do we have columns that have a weight?
+        int restIncludingMinWidths = width - fixedWidth;
+        int rest = restIncludingMinWidths - minWeightWidth;
+        if (numberOfWeightColumns > 0 && rest > 0) {
+
+            // Modify the weights to reflect what each column already
+            // has due to its minimum. Otherwise, columns with low
+            // minimums get discriminated.
+            int totalWantedPixels = 0;
+            int[] wantedPixels = new int[numberOfWeightColumns];
+            for (int i = 0; i < numberOfWeightColumns; i++) {
+                ColumnWeightData cw = cast(ColumnWeightData) getLayoutData(scrollable,weightIteration[i]);
+                wantedPixels[i] = totalWeight is 0 ? 0 : cw.weight
+                        * restIncludingMinWidths / totalWeight;
+                totalWantedPixels += wantedPixels[i];
+            }
+
+            // Now distribute the rest to the columns with weight.
+            int totalDistributed = 0;
+            for (int i = 0; i < numberOfWeightColumns; ++i) {
+                int pixels = totalWantedPixels is 0 ? 0 : wantedPixels[i]
+                        * rest / totalWantedPixels;
+                totalDistributed += pixels;
+                widths[weightIteration[i]] += pixels;
+            }
+
+            // Distribute any remaining pixels to columns with weight.
+            int diff = rest - totalDistributed;
+            for (int i = 0; diff > 0; i = ((i + 1) % numberOfWeightColumns)) {
+                ++widths[weightIteration[i]];
+                --diff;
+            }
+        }
+
+        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 dwt.widgets.Layout#computeSize(dwt.widgets.Composite,
+     *      int, int, bool)
+     */
+    protected Point computeSize(Composite composite, int wHint, int hHint,
+            bool flushCache) {
+        return computeTableTreeSize(getControl(composite), wHint, hHint);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwt.widgets.Layout#layout(dwt.widgets.Composite,
+     *      bool)
+     */
+    protected 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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/layout/TableColumnLayout.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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:
+ *     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 dwtx.jface.layout.TableColumnLayout;
+
+import dwtx.jface.layout.AbstractColumnLayout;
+
+import dwt.widgets.Composite;
+import dwt.widgets.Layout;
+import dwt.widgets.Scrollable;
+import dwt.widgets.Table;
+import dwt.widgets.TableColumn;
+import dwt.widgets.Widget;
+import dwtx.jface.viewers.ColumnLayoutData;
+import dwtx.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 dwtx.jface.layout.AbstractColumnLayout#getColumnCount(dwt.widgets.Scrollable)
+     */
+    int getColumnCount(Scrollable tableTree) {
+        return (cast(Table) tableTree).getColumnCount();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.layout.AbstractColumnLayout#setColumnWidths(dwt.widgets.Scrollable,
+     *      int[])
+     */
+    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 dwtx.jface.layout.AbstractColumnLayout#getLayoutData(int)
+     */
+    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 dwtx.jface.layout.AbstractColumnLayout#updateColumnData(dwt.widgets.Widget)
+     */
+    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/dwtx/jface/util/SafeRunnable.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,213 @@
+/*******************************************************************************
+ * 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 Gross (schtoo@schtoo.com) - support for ISafeRunnableRunner added
+ *       (bug 49497 [RCP] JFace dependency on dwtx.core.runtime enlarges standalone JFace applications)
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.util.SafeRunnable;
+
+import dwtx.jface.util.ISafeRunnableRunner;
+import dwtx.jface.util.SafeRunnableDialog;
+import dwtx.jface.util.Policy;
+
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.widgets.Display;
+import dwtx.core.runtime.ISafeRunnable;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.OperationCanceledException;
+import dwtx.core.runtime.Status;
+import dwtx.jface.resource.JFaceResources;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+
+/**
+ * Implements a default implementation of ISafeRunnable. The default
+ * implementation of <code>handleException</code> opens a message dialog.
+ * <p>
+ * <b>Note:<b> This class can open an error dialog and should not be used
+ * outside of the UI Thread.
+ * </p>
+ */
+public abstract class SafeRunnable : ISafeRunnable {
+
+    private static bool ignoreErrors = false;
+
+    private static ISafeRunnableRunner runner;
+
+    private String message;
+
+    private static SafeRunnableDialog dialog;
+
+    /**
+     * 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 dwtx.core.runtime.ISafeRunnable#handleException(java.lang.Throwable)
+     */
+    public void handleException(Exception e) {
+        // Workaround to avoid interactive error dialogs during automated
+        // testing
+        if (!ignoreErrors) {
+            if (message is null)
+                message = JFaceResources.getString("SafeRunnable.errorMessage"); //$NON-NLS-1$
+
+            Runnable runnable = new class Runnable {
+                IStatus status;
+                this(){
+                    status = new Status(IStatus.ERROR, Policy.JFACE, message,e);
+                }
+                public void run() {
+                    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();
+                    }
+                }
+            };
+            if (Display.getCurrent() !is null) {
+                runnable.run();
+            } else {
+                Display.getDefault().asyncExec(runnable);
+            }
+        }
+    }
+
+    /**
+     * 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);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/util/SafeRunnableDialog.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,351 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.util.SafeRunnableDialog;
+
+pragma(msg,"FIXME dwtx.jface.util.SafeRunnableDialog" );
+import dwt.widgets.Shell;
+import dwtx.core.runtime.IStatus;
+class SafeRunnableDialog{
+    this(IStatus status) ;
+    Shell getShell();
+    void create();
+    void open();
+    void addStatus(IStatus);
+    void refresh();
+}
+
+/++
+
+
+// import java.util.ArrayList;
+// import java.util.Collection;
+
+import dwt.DWT;
+import dwt.graphics.Point;
+import dwt.layout.GridData;
+import dwt.widgets.Button;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwtx.core.runtime.IStatus;
+import dwtx.jface.dialogs.ErrorDialog;
+import dwtx.jface.dialogs.IDialogConstants;
+import dwtx.jface.resource.JFaceResources;
+import dwtx.jface.viewers.CellLabelProvider;
+import dwtx.jface.viewers.ISelection;
+import dwtx.jface.viewers.ISelectionChangedListener;
+import dwtx.jface.viewers.IStructuredContentProvider;
+import dwtx.jface.viewers.IStructuredSelection;
+import dwtx.jface.viewers.SelectionChangedEvent;
+import dwtx.jface.viewers.TableViewer;
+import dwtx.jface.viewers.Viewer;
+import dwtx.jface.viewers.ViewerCell;
+import dwtx.jface.viewers.ViewerComparator;
+
+import dwt.dwthelper.utils;
+
+/**
+ * SafeRunnableDialog is a dialog that can show the results of multiple safe
+ * runnable errors
+ *
+ */
+class SafeRunnableDialog : ErrorDialog {
+
+    private TableViewer statusListViewer;
+
+    private Collection statuses = new ArrayList();
+
+    /**
+     * 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);
+
+        setShellStyle(DWT.DIALOG_TRIM | DWT.MODELESS | DWT.RESIZE | DWT.MIN
+                | getDefaultOrientation());
+
+        setStatus(status);
+        statuses.add(status);
+
+        setBlockOnOpen(false);
+
+        String reason = JFaceResources
+                .getString("SafeRunnableDialog_checkDetailsMessage"); //$NON-NLS-1$
+        if (status.getException() !is null) {
+            reason = status.getException().getMessage() is null ? status
+                    .getException().toString() : status.getException()
+                    .getMessage();
+        }
+        this.message = JFaceResources.format(JFaceResources
+                .getString("SafeRunnableDialog_reason"), new Object[] { //$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((Composite) dialogArea);
+        updateEnablements();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.dialogs.ErrorDialog#createDialogArea(dwt.widgets.Composite)
+     */
+    protected Control createDialogArea(Composite parent) {
+        Control area = super.createDialogArea(parent);
+        createStatusList((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, DWT.SINGLE | DWT.H_SCROLL
+                | DWT.V_SCROLL | DWT.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 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 CellLabelProvider() {
+            /*
+             * (non-Javadoc)
+             *
+             * @see dwtx.jface.viewers.CellLabelProvider#update(dwtx.jface.viewers.ViewerCell)
+             */
+            public void update(ViewerCell cell) {
+                cell.setText(((IStatus) cell.getElement()).getMessage());
+
+            }
+        };
+    }
+
+    /**
+     * Return the content provider for the statuses.
+     *
+     * @return IStructuredContentProvider
+     */
+    private IStructuredContentProvider getStatusContentProvider() {
+        return new IStructuredContentProvider() {
+            /*
+             * (non-Javadoc)
+             *
+             * @see dwtx.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
+             */
+            public Object[] getElements(Object inputElement) {
+                return statuses.toArray();
+            }
+
+            /*
+             * (non-Javadoc)
+             *
+             * @see dwtx.jface.viewers.IContentProvider#dispose()
+             */
+            public void dispose() {
+
+            }
+
+            /*
+             * (non-Javadoc)
+             *
+             * @see dwtx.jface.viewers.IContentProvider#inputChanged(dwtx.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 ViewerComparator() {
+            /*
+             * (non-Javadoc)
+             *
+             * @see dwtx.jface.viewers.ViewerComparator#compare(dwtx.jface.viewers.Viewer,
+             *      java.lang.Object, java.lang.Object)
+             */
+            public int compare(Viewer testViewer, Object e1, Object e2) {
+                String message1 = ((IStatus) e1).getMessage();
+                String message2 = ((IStatus) e2).getMessage();
+                if (message1 is null)
+                    return 1;
+                if (message2 is null)
+                    return -1;
+
+                return message1.compareTo(message2);
+            }
+        };
+    }
+
+    /**
+     * Refresh the contents of the viewer.
+     */
+    void refreshStatusList() {
+        if (statusListViewer !is null
+                && !statusListViewer.getControl().isDisposed()) {
+            statusListViewer.refresh();
+            Point newSize = getShell().computeSize(DWT.DEFAULT, DWT.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
+                && rawSelection instanceof IStructuredSelection) {
+            IStructuredSelection selection = (IStructuredSelection) rawSelection;
+            if (selection.size() is 1) {
+                return (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 dwtx.jface.dialogs.ErrorDialog#shouldShowDetailsButton()
+     */
+    protected bool shouldShowDetailsButton() {
+        return true;
+    }
+
+    /**
+     * Add the status to the receiver.
+     * @param status
+     */
+    public void addStatus(IStatus status) {
+        statuses.add(status);
+        refresh();
+
+    }
+
+
+}
+++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/util/Util.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.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 dwt.dwthelper.utils;
+import dwt.dwthelper.ResourceBundle;
+import tango.core.Exception;
+static import tango.text.Text;
+alias tango.text.Text.Text!(char) StringBuffer;
+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;
+        }
+    }
+
+//     /**
+//      * 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));
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * This class should never be constructed.
+     */
+    private this() {
+        // Not allowed.
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/AbstractListViewer.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,579 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.AbstractListViewer;
+
+import dwtx.jface.viewers.StructuredViewer;
+import dwtx.jface.viewers.ILabelProvider;
+import dwtx.jface.viewers.IBaseLabelProvider;
+import dwtx.jface.viewers.ViewerComparator;
+import dwtx.jface.viewers.IElementComparer;
+
+import tango.util.collection.ArraySeq;
+import tango.util.collection.model.Seq;
+import tango.util.collection.model.SeqView;
+
+import dwt.widgets.Control;
+import dwt.widgets.Widget;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+
+/**
+ * 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 dwtx.jface.viewers.ListViewer
+ * @see dwtx.jface.viewers.ComboViewer
+ *
+ * @since 3.0
+ */
+public abstract class AbstractListViewer : StructuredViewer {
+
+    /**
+     * A list of viewer elements (element type: <code>Object</code>).
+     */
+    private Seq!(Object) 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 ArraySeq!(Object);
+    }
+
+    /**
+     * 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.addAt(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 DWT.List doesn't use items we always return the List itself.
+     */
+    protected Widget doFindInputItem(Object element) {
+        if (element !is null && opEquals(element, getRoot())) {
+            return getControl();
+        }
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StructuredViewer.
+     * Since DWT.List doesn't use items we always return the List itself.
+     */
+    protected Widget doFindItem(Object element) {
+        if (element !is null) {
+            if (listMapContains(element)) {
+                return getControl();
+            }
+        }
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StructuredViewer.
+     */
+    protected 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));
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on Viewer.
+     */
+    public abstract Control getControl();
+
+    /**
+     * 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 IBaseLabelProvider getLabelProvider() {
+        return super.getLabelProvider();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on Viewer.
+     */
+    /* (non-Javadoc)
+     * Method declared on StructuredViewer.
+     */
+    protected SeqView!(Object) getSelectionFromWidget() {
+        int[] ixs = listGetSelectionIndices();
+        ArraySeq!(Object) list = new ArraySeq!(Object);
+        list.capacity(ixs.length);
+        for (int i = 0; i < ixs.length; i++) {
+            Object e = getElementAt(ixs[i]);
+            if (e !is null) {
+                list.append(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 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.append(el);
+            mapElement(el, getControl()); // must map it, since findItem only looks in map, if enabled
+        }
+        listSetItems(labels);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StructuredViewer.
+     */
+    protected void internalRefresh(Object element) {
+        Control list = getControl();
+        if (element is null || opEquals(element, getRoot())) {
+            // the parent
+            if (listMap !is null) {
+                listMap.clear();
+            }
+            unmapAllElements();
+            auto selection = getSelectionFromWidget();
+
+            int topIndex = -1;
+            if (selection is null || selection.drained()) {
+                topIndex = listGetTopIndex();
+            }
+
+            list.setRedraw(false);
+            listRemoveAll();
+
+            Object[] 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.append(el);
+                mapElement(el, list); // must map it, since findItem only looks in map, if enabled
+            }
+
+            listSetItems(items);
+            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.removeAt(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(new class Runnable {
+            Object[] elements_;
+            this(){
+                elements_= elements;
+            }
+            public void run() {
+                internalRemove(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 void setLabelProvider(IBaseLabelProvider labelProvider) {
+        Assert.isTrue( null !is cast(ILabelProvider)labelProvider );
+        super.setLabelProvider(labelProvider);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StructuredViewer.
+     */
+    protected void setSelectionToWidget(SeqView!(Object) 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) {
+            int idx = 0;
+            foreach( e; listMap ){
+                if( e == element ){
+                    return idx;
+                }
+                idx++;
+            }
+            return -1;
+        }
+        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/dwtx/jface/viewers/AbstractTableViewer.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,1362 @@
+/*******************************************************************************
+ * 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation bug 154329
+ *                                               - fixes in bug 170381, 198665
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.viewers.AbstractTableViewer;
+
+import dwtx.jface.viewers.ColumnViewer;
+import dwtx.jface.viewers.ViewerRow;
+import dwtx.jface.viewers.IBaseLabelProvider;
+import dwtx.jface.viewers.IContentProvider;
+import dwtx.jface.viewers.ILazyContentProvider;
+import dwtx.jface.viewers.ViewerColumn;
+import dwtx.jface.viewers.ViewerCell;
+import dwtx.jface.viewers.ViewerComparator;
+import dwtx.jface.viewers.IStructuredContentProvider;
+
+import tango.util.collection.model.SeqView;
+import tango.util.collection.ArraySeq;
+import tango.util.collection.HashSet;
+
+import dwt.DWT;
+import dwt.widgets.Control;
+import dwt.widgets.Event;
+import dwt.widgets.Item;
+import dwt.widgets.Listener;
+import dwt.widgets.Widget;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+import tango.core.Array;
+
+/**
+ * This is a widget independent class implementors of
+ * {@link dwt.widgets.Table} like widgets can use to provide a
+ * viewer on top of their widget implementations.
+ *
+ * @since 3.3
+ */
+public abstract class AbstractTableViewer : ColumnViewer {
+
+    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(DWT.SetData, new class Listener {
+                /*
+                 * (non-Javadoc)
+                 *
+                 * @see dwt.widgets.Listener#handleEvent(dwt.widgets.Event)
+                 */
+                public void handleEvent(Event 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 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 & DWT.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 (isBusy())
+            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(DWT.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 dwtx.jface.viewers.StructuredViewer#doFindInputItem(java.lang.Object)
+     */
+    protected Widget doFindInputItem(Object element) {
+        if (opEquals(element, getRoot())) {
+            return getControl();
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.StructuredViewer#doFindItem(java.lang.Object)
+     */
+    protected 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 dwtx.jface.viewers.StructuredViewer#doUpdateItem(dwt.widgets.Widget,
+     *      java.lang.Object, bool)
+     */
+    protected void doUpdateItem(Widget widget, Object element, bool fullMap) {
+        bool oldBusy = busy;
+        busy = 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() & DWT.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 {
+            busy = oldBusy;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.ColumnViewer#getColumnViewerOwner(int)
+     */
+    protected 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 IBaseLabelProvider getLabelProvider() {
+        return super.getLabelProvider();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.StructuredViewer#getSelectionFromWidget()
+     */
+    protected SeqView!(Object) getSelectionFromWidget() {
+        if (virtualManager !is null) {
+            return getVirtualSelection();
+        }
+        Widget[] items = doGetSelection();
+        auto list = new ArraySeq!(Object);
+        list.capacity(items.length);
+        for (int i = 0; i < items.length; i++) {
+            Widget item = items[i];
+            Object e = item.getData();
+            if (e !is null) {
+                list.append(e);
+            }
+        }
+        return list;
+    }
+
+    /**
+     * Get the virtual selection. Avoid calling DWT whenever possible to prevent
+     * extra widget creation.
+     *
+     * @return List of Object
+     */
+
+    private SeqView!(Object) getVirtualSelection() {
+
+        auto result = new ArraySeq!(Object);
+        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.append(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.append(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 dwtx.jface.viewers.Viewer#inputChanged(java.lang.Object,
+     *      java.lang.Object)
+     */
+    protected void inputChanged(Object input, Object oldInput) {
+        getControl().setRedraw(false);
+        try {
+            preservingSelection(new class Runnable {
+                public void run() {
+                    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 (isBusy())
+            return;
+        createItem(element, position);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.StructuredViewer#internalRefresh(java.lang.Object)
+     */
+    protected void internalRefresh(Object element) {
+        internalRefresh(element, true);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.StructuredViewer#internalRefresh(java.lang.Object,
+     *      bool)
+     */
+    protected 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 = busy;
+                busy = false;
+                try {
+                    setInput(null);
+                } finally {
+                    busy = oldBusy;
+                }
+                return;
+            }
+        }
+        // use remove(int[]) rather than repeated TableItem.dispose() calls
+        // to allow DWT 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 (isBusy())
+            return;
+        if (elements.length is 0) {
+            return;
+        }
+        preservingSelection(new class Runnable {
+            Object[] elements_;
+            this(){
+                elements_=elements;
+            }
+            public void run() {
+                internalRemove(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 dwtx.jface.viewers.StructuredViewer#reveal(java.lang.Object)
+     */
+    public void reveal(Object element) {
+        Assert.isNotNull(element);
+        Widget w = findItem(element);
+        if (auto i = cast(Item)w ) {
+            doShowItem(i);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.StructuredViewer#setSelectionToWidget(java.util.List,
+     *      bool)
+     */
+    protected void setSelectionToWidget(SeqView!(Object) 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 DWT-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;
+            foreach( o; list ) {
+                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.drained() ) {
+                int[] indices = new int[list.size()];
+
+                Item[] items = doGetItems();
+
+                int count = 0;
+                foreach( modelElement; list ){
+                    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(SeqView!(Object) list, bool reveal) {
+        int size = list.size();
+        int[] indices = new int[list.size()];
+
+        Item firstItem = null;
+        int count = 0;
+        auto virtualElements = new HashSet!(Object);
+        foreach( o; list ){
+            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);
+        }
+        doSetSelection(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 (isBusy())
+            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 entries starting at index with elements. This method assumes
+     * all of these values are correct and will not call the content provider to
+     * verify. <strong>Note that this method will create a TableItem for all of
+     * the elements provided</strong>.
+     *
+     * @param element
+     * @param index
+     * @see ILazyContentProvider
+     *
+     * @since 3.1
+     */
+    public void replace(Object element, int index) {
+        if (isBusy())
+            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 dwtx.jface.viewers.StructuredViewer#getRawChildren(java.lang.Object)
+     */
+    protected 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 dwtx.jface.viewers.StructuredViewer#assertContentProviderType(dwtx.jface.viewers.IContentProvider)
+     */
+    protected 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>DWT.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>DWT.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 DWT#VIRTUAL
+     * @see DWT#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/dwtx/jface/viewers/AbstractTreeViewer.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,3026 @@
+/*******************************************************************************
+ * 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - bug 153993, bug 167323, bug 175192
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.viewers.AbstractTreeViewer;
+
+import dwtx.jface.viewers.ColumnViewer;
+import dwtx.jface.viewers.TreePath;
+import dwtx.jface.viewers.ViewerComparator;
+import dwtx.jface.viewers.ITreeViewerListener;
+import dwtx.jface.viewers.TreeExpansionEvent;
+import dwtx.jface.viewers.CustomHashtable;
+import dwtx.jface.viewers.IContentProvider;
+import dwtx.jface.viewers.ISelection;
+import dwtx.jface.viewers.ColumnViewerEditor;
+import dwtx.jface.viewers.ViewerLabel;
+import dwtx.jface.viewers.ViewerRow;
+import dwtx.jface.viewers.TreePathViewerSorter;
+import dwtx.jface.viewers.ViewerFilter;
+import dwtx.jface.viewers.ViewerColumn;
+import dwtx.jface.viewers.ViewerCell;
+import dwtx.jface.viewers.ITreePathContentProvider;
+import dwtx.jface.viewers.ITreeContentProvider;
+import dwtx.jface.viewers.TreeSelection;
+import dwtx.jface.viewers.DoubleClickEvent;
+import dwtx.jface.viewers.IElementComparer;
+import dwtx.jface.viewers.ITreeSelection;
+import dwtx.jface.viewers.IBaseLabelProvider;
+import dwtx.jface.viewers.ITreePathLabelProvider;
+
+import tango.util.collection.LinkSeq;
+import tango.util.collection.ArraySeq;
+import tango.util.collection.model.Seq;
+import tango.util.collection.model.SeqView;
+
+
+import dwt.DWT;
+import dwt.custom.BusyIndicator;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.events.TreeEvent;
+import dwt.events.TreeListener;
+import dwt.graphics.Point;
+import dwt.widgets.Control;
+import dwt.widgets.Item;
+import dwt.widgets.Widget;
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.ListenerList;
+import dwtx.jface.util.SafeRunnable;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+
+/**
+ * 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.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 (isBusy())
+            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, DWT.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) {
+            auto filtered = new ArraySeq!(Object);
+            filtered.capacity(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.append(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);
+
+        // As the items are sorted already we optimize for a
+        // start position
+        int lastInsertion = 0;
+
+        // Optimize for the empty case
+        if (items.length is 0) {
+            for (int i = 0; i < elements.length; i++) {
+                createTreeItem(widget, elements[i], -1);
+            }
+            return;
+        }
+
+        for (int i = 0; i < elements.length; i++) {
+            bool newItem = true;
+            Object element = elements[i];
+            int index;
+            if (comparator is null) {
+                if (itemExists(items, element)) {
+                    internalRefresh(element);
+                    newItem = false;
+                }
+                index = -1;
+            } else {
+                lastInsertion = insertionPosition(items, comparator,
+                        lastInsertion, element, parentPath);
+                // As we are only searching the original array we keep track of
+                // those positions only
+                if (lastInsertion is items.length) {
+                    index = -1;
+                } else {// See if we should just refresh
+                    while (lastInsertion < items.length
+                            && internalCompare(comparator, parentPath, element,
+                                    items[lastInsertion].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[lastInsertion].getData().opEquals(element)) {
+                            // refresh the element in case it has new children
+                            internalRefresh(element);
+                            newItem = false;
+                        }
+                        lastInsertion++;// We had an insertion so increment
+                    }
+                    // Did we get to the end?
+                    if (lastInsertion is items.length) {
+                        index = -1;
+                    } else {
+                        index = lastInsertion + i; // Add the index as the
+                                                    // array is growing
+                    }
+                }
+            }
+            if (newItem) {
+                createTreeItem(widget, element, index);
+            }
+        }
+    }
+
+    /**
+     * 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 dwtx.jface.viewers.StructuredViewer#getSortedChildren(java.lang.Object)
+     */
+    protected 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 dwtx.jface.viewers.StructuredViewer#getFilteredChildren(java.lang.Object)
+     */
+    protected 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 DWT selection listener to the given DWT control.
+     *
+     * @param control
+     *            the DWT control
+     * @param listener
+     *            the DWT 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 DWT tree listener to the given DWT control.
+     *
+     * @param control
+     *            the DWT control
+     * @param listener
+     *            the DWT tree listener
+     */
+    protected abstract void addTreeListener(Control control,
+            TreeListener listener);
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see StructuredViewer#associate(Object, Item)
+     */
+    protected 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 = busy;
+        busy = true;
+        try {
+            final 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(), new class Runnable {
+                Widget widget_;
+                Item[] tis_;
+                this(){
+                    widget_ = widget;
+                    tis_=tis;
+                }
+                public void run() {
+                    // 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);
+                        }
+                    }
+                }
+
+            });
+        } finally {
+            busy = 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, DWT.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 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 DWT 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 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 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 DWT item.
+     *
+     * @param item
+     *            the DWT 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() & DWT.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(SeqView!(Item) 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);
+        foreach( item; items ){
+            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 void doUpdateItem(Widget widget, Object element, bool fullMap) {
+        bool oldBusy = busy;
+        busy = 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 {
+            busy = 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 (isBusy())
+            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();
+        for (int i = 0; i < listeners.length; ++i) {
+            SafeRunnable.run(new class SafeRunnable {
+                TreeExpansionEvent event_;
+                ITreeViewerListener l;
+                this(){
+                    event_=event;
+                    l = cast(ITreeViewerListener) listeners[i];
+                }
+                public void run() {
+                    l.treeCollapsed(event_);
+                }
+            });
+        }
+    }
+
+    /**
+     * 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();
+        for (int i = 0; i < listeners.length; ++i) {
+            SafeRunnable.run(new class SafeRunnable {
+                TreeExpansionEvent event_;
+                ITreeViewerListener l;
+                this(){
+                    event_=event;
+                    l = cast(ITreeViewerListener) listeners[i];
+                }
+                public void run() {
+                    l.treeExpanded(event_);
+                }
+            });
+        }
+
+    }
+
+    /**
+     * 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 DWT child items for the given DWT 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 DWT 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() {
+        auto items = new ArraySeq!(Item);
+        internalCollectExpandedItems(items, getControl());
+        auto result = new ArraySeq!(Object);
+        result.capacity(items.size());
+        foreach ( item; items ) {
+            Object data = item.getData();
+            if (data !is null) {
+                result.append(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 DWT 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 DWT item.
+     *
+     * @param item
+     *            the item
+     * @return the number of children
+     */
+    protected abstract int getItemCount(Item item);
+
+    /**
+     * Returns the child items of the given DWT 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 Object[] getRawChildren(Object parentElementOrTreePath) {
+        bool oldBusy = busy;
+        busy = 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 {
+            busy = oldBusy;
+        }
+    }
+
+    /**
+     * Returns all selected items for the given DWT control.
+     *
+     * @param control
+     *            the control
+     * @return the list of selected items
+     */
+    protected abstract Item[] getSelection(Control control);
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.StructuredViewer#getSelectionFromWidget()
+     */
+    protected SeqView!(Object) getSelectionFromWidget() {
+        Widget[] items = getSelection(getControl());
+        ArraySeq!(Object) list = new ArraySeq!(Object);
+        list.capacity(items.length);
+        for (int i = 0; i < items.length; i++) {
+            Widget item = items[i];
+            Object e = item.getData();
+            if (e !is null) {
+                list.append(e);
+            }
+        }
+        return list;
+    }
+
+    /*
+     * Overridden in AbstractTreeViewer to fix bug 108102 (code copied from
+     * StructuredViewer to avoid introducing new API) (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.StructuredViewer#handleDoubleSelect(dwt.events.SelectionEvent)
+     */
+    protected 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 DWT widget.
+     *
+     * @param event
+     *            the DWT 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 DWT widget.
+     *
+     * @param event
+     *            the DWT 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 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 void inputChanged(Object input, Object oldInput) {
+        preservingSelection(new class Runnable {
+            public void run() {
+                Control tree = getControl();
+                bool useRedraw = true;
+                // (size > REDRAW_THRESHOLD) || (table.getItemCount() >
+                // REDRAW_THRESHOLD);
+                if (useRedraw) {
+                    tree.setRedraw(false);
+                }
+                removeAll(tree);
+                tree.setData(getRoot());
+                internalInitializeTree(tree);
+                if (useRedraw) {
+                    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(Seq!(Item) result, Widget widget) {
+        Item[] items = getChildren(widget);
+        for (int i = 0; i < items.length; i++) {
+            Item item = items[i];
+            if (getExpanded(item)) {
+                result.append(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;
+                        auto toExpandList = new LinkSeq!(Item);
+                        while (item !is null && !getExpanded(item)) {
+                            toExpandList.prepend(item);
+                            item = getParentItem(item);
+                        }
+                        foreach( toExpand; toExpandList ){
+                            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 getInput();
+                }
+                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 void internalRefresh(Object element) {
+        internalRefresh(element, true);
+    }
+
+    /* (non-Javadoc) Method declared on StructuredViewer. */
+    protected 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);
+            for (int j = 0; j < childItems.length; j++) {
+                Widget childItem = childItems[j];
+                if ( auto it = cast(Item)childItem ) {
+                    disassociate(it);
+                    childItem.dispose();
+                }
+            }
+        }
+    }
+
+    /**
+     * 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];
+
+            // Iterate over the child items and remove each one
+            Item[] children = getChildren(parentItem);
+
+            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 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
+     *            DWT 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 (isBusy())
+            return;
+        preservingSelection(new class Runnable {
+            Object[] elementsOrTreePaths_;
+            this(){
+                elementsOrTreePaths_=elementsOrTreePaths;
+            }
+            public void run() {
+                internalRemove(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 (isBusy())
+            return;
+        preservingSelection(new class Runnable {
+            Object parent_;
+            Object[] elements_;
+            this(){
+                parent_=parent;
+                elements_=elements;
+            }
+            public void run() {
+                internalRemove(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 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 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 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. The value 0 means that there is no
+     * auto-expand; 1 means that top-level elements are expanded, but not their
+     * children; 2 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>
+     *
+     * @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 void setContentProvider(IContentProvider provider) {
+        // the actual check is in assertContentProviderType
+        super.setContentProvider(provider);
+    }
+
+    protected 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 (isBusy()) {
+            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 (isBusy())
+            return;
+        IElementComparer treePathComparer = new class IElementComparer {
+            IElementComparer comparer;
+            this(){
+                comparer = getComparer();
+            }
+            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 (isBusy())
+            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>dwt.widgets.Item</code>)
+     */
+    protected abstract void setSelection(SeqView!(Item) items);
+
+    /**
+     * This implementation of setSelectionToWidget accepts a list of elements or
+     * a list of tree paths.
+     */
+    protected void setSelectionToWidget(SeqView!(Object) v, bool reveal) {
+        if (v is null) {
+            setSelection(new ArraySeq!(Item));
+            return;
+        }
+        int size = v.size();
+        auto newSelection = new ArraySeq!(Item);
+        newSelection.capacity(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.append(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.append(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 the first item in the selection here.
+        // See bug 100565 for more details.
+        if (reveal && newSelection.size() > 0) {
+            showItem(cast(Item) newSelection.get(0));
+        }
+    }
+
+    /**
+     * 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, DWT.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);
+
+        // 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);
+        }
+    }
+
+    /**
+     * 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, DWT.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() {
+        auto v = new ArraySeq!(Object);
+        internalCollectVisibleExpanded(v, getControl());
+        return v.toArray();
+    }
+
+    private void internalCollectVisibleExpanded(ArraySeq!(Object) 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.append(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) {
+        auto segments = new LinkSeq!(Object);
+        while (item !is null) {
+            Object segment = item.getData();
+            Assert.isNotNull(segment);
+            segments.prepend(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 ISelection getSelection() {
+        Control control = getControl();
+        if (control is null || control.isDisposed()) {
+            return TreeSelection.EMPTY;
+        }
+        Widget[] items = getSelection(getControl());
+        auto list = new ArraySeq!(TreePath);
+        list.capacity(items.length);
+        for (int i = 0; i < items.length; i++) {
+            Widget item = items[i];
+            if (item.getData() !is null) {
+                list.append(getTreePathFromItem(cast(Item) item));
+            }
+        }
+        return new TreeSelection( list.toArray(), getComparer());
+    }
+
+    protected void setSelectionToWidget(ISelection selection, bool reveal) {
+        if ( auto treeSelection = cast(ITreeSelection)selection ) {
+            auto list = new ArraySeq!(Object);
+            auto paths = treeSelection.getPaths();
+            list.capacity(paths.length);
+            foreach( path; paths ){
+                list.append(path);
+            }
+            setSelectionToWidget(list, 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() {
+        auto items = new ArraySeq!(Item);
+        internalCollectExpandedItems(items, getControl());
+        auto result = new ArraySeq!(TreePath);
+        result.capacity(items.size());
+        foreach( item; items ){
+            TreePath treePath = getTreePathFromItem(item);
+            if (treePath !is null) {
+                result.append(treePath);
+            }
+        }
+        return 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 (isBusy())
+            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 {
+                int insertionPosition = position;
+                if (insertionPosition is -1) {
+                    insertionPosition = getItemCount(cast(Control) widget);
+                }
+
+                createTreeItem(widget, element, insertionPosition);
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.ColumnViewer#getColumnViewerOwner(int)
+     */
+    protected 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 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 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 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 dwtx.jface.viewers.StructuredViewer#buildLabel(dwtx.jface.viewers.ViewerLabel,
+     *      java.lang.Object)
+     */
+    protected 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(getInput()))
+            return true;
+        if (!(cast(TreePath)elementOrTreePath ))
+            return false;
+        return (cast(TreePath) elementOrTreePath).getSegmentCount() is 0;
+    }
+
+    /*
+     * Subclasses should implement
+     */
+    protected ViewerRow getViewerRowFromItem(Widget item) {
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/AcceptAllFilter.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.AcceptAllFilter;
+
+import dwtx.jface.viewers.IFilter;
+
+import dwt.dwthelper.utils;
+
+
+/**
+ * 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 dwtx.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 bool equals(Object other) {
+        return other is this || null !is cast(AcceptAllFilter)other ;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/ArrayContentProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ArrayContentProvider;
+
+import dwtx.jface.viewers.IStructuredContentProvider;
+import dwtx.jface.viewers.Viewer;
+
+import tango.util.collection.model.View;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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
+ */
+public class ArrayContentProvider : 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(ArrayWrapperObject) inputElement ) {
+            return aw.array;
+        }
+        if ( auto col = cast(View!(Object)) 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/dwtx/jface/viewers/BaseLabelProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.BaseLabelProvider;
+
+import dwtx.jface.viewers.IBaseLabelProvider;
+import dwtx.jface.viewers.ILabelProviderListener;
+import dwtx.jface.viewers.LabelProviderChangedEvent;
+
+import dwtx.core.commands.common.EventManager;
+import dwtx.jface.util.SafeRunnable;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 dwtx.jface.viewers.IBaseLabelProvider#removeListener(dwtx.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 SafeRunnable {
+                LabelProviderChangedEvent event_;
+                ILabelProviderListener l;
+                this(){
+                    event_=event;
+                    l = cast(ILabelProviderListener) listeners[i];
+                }
+                public void run() {
+                    l.labelProviderChanged(event_);
+                }
+            });
+
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/CellEditor.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,907 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.viewers.CellEditor;
+
+import dwtx.jface.viewers.ICellEditorValidator;
+import dwtx.jface.viewers.ICellEditorListener;
+import dwtx.jface.viewers.ColumnViewerEditorActivationEvent;
+
+import dwt.DWT;
+import dwt.events.KeyEvent;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.ListenerList;
+import dwtx.jface.util.IPropertyChangeListener;
+import dwtx.jface.util.PropertyChangeEvent;
+import dwtx.jface.util.SafeRunnable;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Abstract base class for cell editors. Implements property change listener handling,
+ * and DWT 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 = DWT.NONE;
+
+    /**
+     * This cell editor's style
+     */
+    private int style = defaultStyle;
+
+    /**
+     * Struct-like layout data for cell editors, with reasonable defaults
+     * for all fields.
+     */
+    public static class LayoutData {
+        /**
+         * Horizontal alignment; <code>DWT.LEFT</code> by default.
+         */
+        public int horizontalAlignment = DWT.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;
+    }
+
+    /**
+     * 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()istrue 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 DWT 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 SafeRunnable {
+                ICellEditorListener l;
+                this(){
+                    l = cast(ICellEditorListener) array[i];
+                }
+                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 SafeRunnable {
+                ICellEditorListener l;
+                this(){
+                    l = cast(ICellEditorListener) array[i];
+                }
+                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 SafeRunnable {
+                bool newValidState_;
+                bool oldValidState_;
+                ICellEditorListener l;
+                this(){
+                    newValidState_=newValidState;
+                    oldValidState_=oldValidState;
+                    l = cast(ICellEditorListener) array[i];
+                }
+                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 SafeRunnable {
+                String actionId_;
+                IPropertyChangeListener l;
+                this(){
+                    actionId_=actionId;
+                    l = cast(IPropertyChangeListener) array[i];
+                }
+                public void run() {
+                    l.propertyChange(new PropertyChangeEvent(this, actionId_,
+                            null, null));
+                }
+            });
+        }
+    }
+
+    /**
+     * Sets the style bits for this cell editor.
+     *
+     * @param style the DWT 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 DWT 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(DWT.DEFAULT, DWT.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();
+    }
+
+    /**
+     * This method is for interal use in {@link ColumnViewerEditor} to not break clients
+     * who don't implement the {@link ICellEditorListener} appropiately
+     *
+     * @return <code>true</code> to indicate that a focus listener has to be attached
+     */
+    bool dependsOnExternalFocusListener() {
+        return true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/CellLabelProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,233 @@
+/*******************************************************************************
+ * 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
+ *                                              - bug fixes for 182443
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.viewers.CellLabelProvider;
+
+import dwtx.jface.viewers.IBaseLabelProvider;
+import dwtx.jface.viewers.BaseLabelProvider;
+import dwtx.jface.viewers.ColumnViewer;
+import dwtx.jface.viewers.ViewerCell;
+import dwtx.jface.viewers.AbstractTreeViewer;
+import dwtx.jface.viewers.ITableLabelProvider;
+import dwtx.jface.viewers.ITableColorProvider;
+import dwtx.jface.viewers.ITableFontProvider;
+import dwtx.jface.viewers.TableColumnViewerLabelProvider;
+import dwtx.jface.viewers.WrappedViewerLabelProvider;
+
+import dwt.DWT;
+import dwt.custom.CLabel;
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 {
+
+    /**
+     * 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 ))
+            return new TableColumnViewerLabelProvider(labelProvider);
+        if ( cast(CellLabelProvider)labelProvider )
+            return cast(CellLabelProvider) labelProvider;
+        return new WrappedViewerLabelProvider(labelProvider);
+
+    }
+
+    /**
+     * 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 DWT#COLOR_INFO_BACKGROUND}
+     * @see DWT#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 DWT#COLOR_INFO_FOREGROUND}
+     * @see DWT#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 DWT} style used to create the {@link CLabel} (see there for
+     * supported styles). By default {@link DWT#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 DWT.SHADOW_NONE;
+    }
+
+    /**
+     * Update the label for cell.
+     *
+     * @param cell
+     *            {@link ViewerCell}
+     */
+    public abstract void update(ViewerCell cell);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/CellNavigationStrategy.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ ******************************************************************************/
+
+module dwtx.jface.viewers.CellNavigationStrategy;
+
+import dwtx.jface.viewers.ColumnViewer;
+import dwtx.jface.viewers.ViewerCell;
+
+import dwt.DWT;
+import dwt.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 DWT.ARROW_UP:
+        case DWT.ARROW_DOWN:
+        case DWT.ARROW_LEFT:
+        case DWT.ARROW_RIGHT:
+        case DWT.HOME:
+        case DWT.PAGE_DOWN:
+        case DWT.PAGE_UP:
+        case DWT.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 DWT.ARROW_UP:
+            if (currentSelectedCell !is null) {
+                return currentSelectedCell.getNeighbor(ViewerCell.ABOVE, false);
+            }
+            break;
+        case DWT.ARROW_DOWN:
+            if (currentSelectedCell !is null) {
+                return currentSelectedCell.getNeighbor(ViewerCell.BELOW, false);
+            }
+            break;
+        case DWT.ARROW_LEFT:
+            if (currentSelectedCell !is null) {
+                return currentSelectedCell.getNeighbor(ViewerCell.LEFT, true);
+            }
+            break;
+        case DWT.ARROW_RIGHT:
+            if (currentSelectedCell !is null) {
+                return currentSelectedCell.getNeighbor(ViewerCell.RIGHT, true);
+            }
+            break;
+        }
+
+        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 DWT.ARROW_LEFT
+                || event.keyCode is DWT.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/dwtx/jface/viewers/CheckStateChangedEvent.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.CheckStateChangedEvent;
+
+import dwtx.jface.viewers.ICheckStateListener;
+import dwtx.jface.viewers.ICheckable;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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/dwtx/jface/viewers/CheckboxCellEditor.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.CheckboxCellEditor;
+
+import dwtx.jface.viewers.CellEditor;
+import dwtx.jface.viewers.ColumnViewerEditorActivationEvent;
+
+import dwt.DWT;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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>
+ */
+public class CheckboxCellEditor : CellEditor {
+
+    /**
+     * The checkbox value.
+     */
+    /* package */
+    bool value = false;
+
+    /**
+     * Default CheckboxCellEditor style
+     */
+    private static const int defaultStyle = DWT.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 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 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>bool</code>.
+     *
+     * @return the bool checkbox value
+     */
+    protected Object doGetValue() {
+        return new ValueWrapperBool( value );
+    }
+
+    /* (non-Javadoc)
+     * Method declared on CellEditor.
+     */
+    protected void doSetFocus() {
+        // Ignore
+    }
+
+    /**
+     * The <code>CheckboxCellEditor</code> implementation of
+     * this <code>CellEditor</code> framework method accepts
+     * a value wrapped as a <code>bool</code>.
+     *
+     * @param value a bool value
+     */
+    protected void doSetValue(Object value) {
+        Assert.isTrue( null !is cast(ValueWrapperBool)value );
+        this.value = (cast(ValueWrapperBool) value).value;
+    }
+
+    public void activate(ColumnViewerEditorActivationEvent activationEvent) {
+        if (activationEvent.eventType !is ColumnViewerEditorActivationEvent.TRAVERSAL) {
+            super.activate(activationEvent);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/CheckboxTableViewer.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,462 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.CheckboxTableViewer;
+
+import dwtx.jface.viewers.ICheckable;
+import dwtx.jface.viewers.TableViewer;
+import dwtx.jface.viewers.ICheckStateListener;
+import dwtx.jface.viewers.CheckStateChangedEvent;
+import dwtx.jface.viewers.TableLayout;
+import dwtx.jface.viewers.ColumnWeightData;
+import dwtx.jface.viewers.CustomHashtable;
+
+import tango.util.collection.ArraySeq;
+import tango.util.collection.model.Seq;
+
+import dwt.DWT;
+import dwt.events.SelectionEvent;
+import dwt.widgets.Composite;
+import dwt.widgets.Table;
+import dwt.widgets.TableColumn;
+import dwt.widgets.TableItem;
+import dwt.widgets.Widget;
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.ListenerList;
+import dwtx.jface.util.SafeRunnable;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+
+/**
+ * A concrete viewer based on an DWT <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 DWT table control and configured
+ * with a domain-specific content provider, label provider, element filter (optional),
+ * and element sorter (optional).
+ * </p>
+ */
+public class CheckboxTableViewer : TableViewer, ICheckable {
+
+    /**
+     * 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 DWT style bits:
+     * <code>DWT.CHECK</code> and <code>DWT.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, DWT.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, DWT.BORDER);
+    }
+
+    /**
+     * Creates a table viewer on a newly-created table control under the given parent.
+     * The table control is created using the given DWT style bits, plus the
+     * <code>DWT.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 DWT 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>DWT.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 DWT 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 DWT style bits, plus the
+     * <code>DWT.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. DWT does not require a
+     * <code>TableColumn</code> if showing only a single column with no header.
+     * DWT correctly handles the initial sizing and subsequent resizes in this case.
+     *
+     * @param parent the parent control
+     * @param style DWT style bits
+     *
+     * @since 2.0
+     * @return CheckboxTableViewer
+     */
+    public static CheckboxTableViewer newCheckList(Composite parent, int style) {
+        Table table = new Table(parent, DWT.CHECK | style);
+        return new CheckboxTableViewer(table);
+    }
+
+    /**
+     * Creates a table viewer on the given table control.
+     * The <code>DWT.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, DWT.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, DWT.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(new class SafeRunnable {
+                ICheckStateListener l;
+                CheckStateChangedEvent event_;
+                this(){
+                    event_=event;
+                    l = cast(ICheckStateListener) array[i];
+                }
+                public void run() {
+                    l.checkStateChanged(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();
+        auto v = new ArraySeq!(Object);
+        v.capacity(children.length);
+        for (int i = 0; i < children.length; i++) {
+            TableItem item = children[i];
+            if (item.getChecked()) {
+                v.append(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();
+        auto v = new ArraySeq!(Object);
+        v.capacity(children.length);
+        for (int i = 0; i < children.length; i++) {
+            TableItem item = children[i];
+            if (item.getGrayed()) {
+                v.append(item.getData());
+            }
+        }
+        return v.toArray();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StructuredViewer.
+     */
+    public void handleSelect(SelectionEvent event) {
+        if (event.detail is DWT.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 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/dwtx/jface/viewers/CheckboxTreeViewer.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,601 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.CheckboxTreeViewer;
+
+import dwtx.jface.viewers.TreeViewer;
+import dwtx.jface.viewers.ICheckable;
+import dwtx.jface.viewers.ICheckStateListener;
+import dwtx.jface.viewers.CustomHashtable;
+import dwtx.jface.viewers.CheckStateChangedEvent;
+
+import tango.util.collection.ArraySeq;
+import tango.util.collection.model.Seq;
+
+import dwt.DWT;
+import dwt.events.SelectionEvent;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Item;
+import dwt.widgets.Tree;
+import dwt.widgets.TreeItem;
+import dwt.widgets.Widget;
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.ListenerList;
+import dwtx.jface.util.SafeRunnable;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+
+/**
+ * A concrete tree-structured viewer based on an DWT <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 DWT tree control and configured
+ * with a domain-specific content provider, label provider, element filter (optional),
+ * and element sorter (optional).
+ * </p>
+ */
+public class CheckboxTreeViewer : TreeViewer, ICheckable {
+
+    /**
+     * 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 DWT 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, DWT.BORDER);
+    }
+
+    /**
+     * Creates a tree viewer on a newly-created tree control under the given parent.
+     * The tree control is created using the given DWT 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 DWT style bits
+     */
+    public this(Composite parent, int style) {
+        this(new Tree(parent, DWT.CHECK | style));
+    }
+
+    /**
+     * Creates a tree viewer on the given tree control.
+     * The <code>DWT.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(new class SafeRunnable {
+                ICheckStateListener l;
+                this(){
+                    l = cast(ICheckStateListener) array[i];
+                }
+                public void run() {
+                    l.checkStateChanged(event);
+                }
+            });
+        }
+
+    }
+
+    /**
+     * 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() {
+        ArraySeq!(Object) v = new ArraySeq!(Object);
+        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() {
+        ArraySeq!(Object) result = new ArraySeq!(Object);
+        internalCollectGrayed(result, getControl());
+        return result.toArray();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StructuredViewer.
+     */
+    protected 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 void handleSelect(SelectionEvent event) {
+
+        lastClickedItem = null;
+        if (event.detail is DWT.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(Seq!(Object) 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.append(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(Seq!(Object) 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.append(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 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.
+     *
+     * @param state <code>true</code> if the element should be checked,
+     *  and <code>false</code> if it should be unchecked
+     *
+     *  @since 3.2
+     */
+    public void setAllChecked(bool state) {
+        setAllChecked(state,  getTree().getItems());
+
+    }
+
+    /**
+     * Set the checked state of items and their children to state.
+     * @param state
+     * @param items
+     */
+    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/dwtx/jface/viewers/ColorCellEditor.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,253 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.ColorCellEditor;
+
+import dwtx.jface.viewers.DialogCellEditor;
+
+import dwt.DWT;
+import dwt.custom.TableTree;
+import dwt.graphics.Color;
+import dwt.graphics.FontMetrics;
+import dwt.graphics.GC;
+import dwt.graphics.Image;
+import dwt.graphics.ImageData;
+import dwt.graphics.PaletteData;
+import dwt.graphics.Point;
+import dwt.graphics.RGB;
+import dwt.graphics.Rectangle;
+import dwt.widgets.ColorDialog;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Label;
+import dwt.widgets.Layout;
+import dwt.widgets.Table;
+import dwt.widgets.Tree;
+
+import dwt.dwthelper.utils;
+import tango.text.convert.Format;
+
+/**
+ * A cell editor that manages a color field.
+ * The cell editor's value is the color (an DWT <code>RBG</code>).
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed.
+ * </p>
+ */
+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 Point computeSize(Composite editor, int wHint, int hHint,
+                bool force) {
+            if (wHint !is DWT.DEFAULT && hHint !is DWT.DEFAULT) {
+                return new Point(wHint, hHint);
+            }
+            Point colorSize = colorLabel.computeSize(DWT.DEFAULT, DWT.DEFAULT,
+                    force);
+            Point rgbSize = rgbLabel.computeSize(DWT.DEFAULT, DWT.DEFAULT,
+                    force);
+            return new Point(colorSize.x + GAP + rgbSize.x, Math.max(
+                    colorSize.y, rgbSize.y));
+        }
+
+        public void layout(Composite editor, bool force) {
+            Rectangle bounds = editor.getClientArea();
+            Point colorSize = colorLabel.computeSize(DWT.DEFAULT, DWT.DEFAULT,
+                    force);
+            Point rgbSize = rgbLabel.computeSize(DWT.DEFAULT, DWT.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, DWT.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 Control createContents(Composite cell) {
+        Color bg = cell.getBackground();
+        composite = new Composite(cell, getStyle());
+        composite.setBackground(bg);
+        composite.setLayout(new ColorCellLayout());
+        colorLabel = new Label(composite, DWT.LEFT);
+        colorLabel.setBackground(bg);
+        rgbLabel = new Label(composite, DWT.LEFT);
+        rgbLabel.setBackground(bg);
+        rgbLabel.setFont(cell.getFont());
+        return composite;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on CellEditor.
+     */
+    public void dispose() {
+        if (image !is null) {
+            image.dispose();
+            image = null;
+        }
+        super.dispose();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on DialogCellEditor.
+     */
+    protected 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 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: DWT: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/dwtx/jface/viewers/ColumnLabelProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.ColumnLabelProvider;
+
+import dwtx.jface.viewers.CellLabelProvider;
+import dwtx.jface.viewers.IFontProvider;
+import dwtx.jface.viewers.IColorProvider;
+import dwtx.jface.viewers.ILabelProvider;
+import dwtx.jface.viewers.ViewerCell;
+
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.Image;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 dwtx.jface.viewers.CellLabelProvider#update(dwtx.jface.viewers.ViewerCell)
+     */
+    public 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 dwtx.jface.viewers.IFontProvider#getFont(java.lang.Object)
+     */
+    public Font getFont(Object element) {
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.IColorProvider#getBackground(java.lang.Object)
+     */
+    public Color getBackground(Object element) {
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.IColorProvider#getForeground(java.lang.Object)
+     */
+    public Color getForeground(Object element) {
+        return null;
+    }
+
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ILabelProvider#getImage(java.lang.Object)
+     */
+    public Image getImage(Object element) {
+        return null;
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.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/dwtx/jface/viewers/ColumnLayoutData.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ColumnLayoutData;
+
+/**
+ * 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>
+ */
+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/dwtx/jface/viewers/ColumnPixelData.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ColumnPixelData;
+
+import dwtx.jface.viewers.ColumnLayoutData;
+
+import dwtx.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>
+ */
+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/dwtx/jface/viewers/ColumnViewer.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,769 @@
+/*******************************************************************************
+ * 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; bug 153993
+ *                                                 fix in bug 163317, 151295, 167323, 167858, 184346, 187826, 200558,201002
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.viewers.ColumnViewer;
+
+import dwtx.jface.viewers.StructuredViewer;
+import dwtx.jface.viewers.CellEditor;
+import dwtx.jface.viewers.ICellModifier;
+import dwtx.jface.viewers.ColumnViewerEditor;
+import dwtx.jface.viewers.ViewerCell;
+import dwtx.jface.viewers.ViewerRow;
+import dwtx.jface.viewers.ViewerColumn;
+import dwtx.jface.viewers.CellLabelProvider;
+import dwtx.jface.viewers.IBaseLabelProvider;
+import dwtx.jface.viewers.ColumnViewerEditorActivationEvent;
+import dwtx.jface.viewers.EditingSupport;
+import dwtx.jface.viewers.ITableLabelProvider;
+import dwtx.jface.viewers.ILabelProvider;
+import dwtx.jface.viewers.StructuredSelection;
+
+import dwt.events.MouseAdapter;
+import dwt.events.MouseEvent;
+import dwt.graphics.Point;
+import dwt.widgets.Control;
+import dwt.widgets.Item;
+import dwt.widgets.Widget;
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.IStatus;
+import dwtx.core.runtime.Status;
+import dwtx.jface.internal.InternalPolicy;
+import dwtx.jface.util.Policy;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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;
+
+    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;
+
+    /* package */ bool busy;
+    /* package */ 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);
+    }
+
+    /* package */ bool isBusy() {
+        if (busy) {
+            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;
+    }
+
+    protected 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
+     */
+    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) {
+            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) EditingSupport {
+                int columnIndex_;
+                this(ColumnViewer cv){
+                    super(cv);
+                    columnIndex_=columnIndex;
+                }
+                /*
+                 * (non-Javadoc)
+                 *
+                 * @see dwtx.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 dwtx.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 dwtx.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 dwtx.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. Be careful not
+     * to hold on to element objects longer than required. It is good practice to
+     * call updateCell(null, 0, null) to clear references immediately after using
+     * the cached cell object. (See bug 201280 for an example case where this happened.)
+     *
+     * @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 dwtx.jface.viewers.StructuredViewer#getItem(int, int)
+     */
+    protected 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 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);
+    }
+
+    /**
+     * 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 void refresh(Object element) {
+        if (isBusy())
+            return;
+
+        if( isCellEditorActive() ) {
+            cancelEditing();
+        }
+
+        super.refresh(element);
+    }
+
+    public void refresh(Object element, bool updateLabels) {
+        if (isBusy())
+            return;
+
+        if( isCellEditorActive() ) {
+            cancelEditing();
+        }
+
+        super.refresh(element, updateLabels);
+    }
+
+    public void update(Object element, String[] properties) {
+        if (isBusy())
+            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>
+     *
+     * @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>
+     *
+     * @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>
+     *
+     * @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) {
+            return column.getLabelProvider();
+        }
+        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(viewerEditor);
+        this.viewerEditor = columnViewerEditor;
+    }
+
+    /**
+     * @return the currently attached viewer editor
+     */
+    public ColumnViewerEditor getColumnViewerEditor() {
+        return viewerEditor;
+    }
+
+    protected Object[] getRawChildren(Object parent) {
+        bool oldBusy = busy;
+        busy = true;
+        try {
+            return super.getRawChildren(parent);
+        } finally {
+            busy = oldBusy;
+        }
+    }
+
+    /**
+     * Clear all cell-editors setup for backwards compatibility in
+     * {@link #setupEditingSupport(int, ViewerColumn)}. This has to be done
+     * whenever a column is disposed because the index cached when the anonymous
+     * class is created has to be readjusted
+     */
+    void clearLegacyEditingSetup() {
+        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);
+                    }
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/ColumnViewerEditor.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,658 @@
+/*******************************************************************************
+ * 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,166500,200337
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.viewers.ColumnViewerEditor;
+
+import dwtx.jface.viewers.CellEditor;
+import dwtx.jface.viewers.ICellEditorListener;
+import dwtx.jface.viewers.ColumnViewer;
+import dwtx.jface.viewers.ColumnViewerEditorActivationStrategy;
+import dwtx.jface.viewers.ViewerCell;
+import dwtx.jface.viewers.ViewerColumn;
+import dwtx.jface.viewers.ColumnViewerEditorActivationEvent;
+import dwtx.jface.viewers.ColumnViewerEditorActivationListener;
+import dwtx.jface.viewers.ColumnViewerEditorDeactivationEvent;
+import dwtx.jface.viewers.ViewerRow;
+import dwtx.jface.viewers.DoubleClickEvent;
+import dwtx.jface.viewers.OpenEvent;
+
+import dwt.DWT;
+import dwt.events.FocusAdapter;
+import dwt.events.FocusEvent;
+import dwt.events.FocusListener;
+import dwt.events.MouseAdapter;
+import dwt.events.MouseEvent;
+import dwt.events.MouseListener;
+import dwt.events.TraverseEvent;
+import dwt.events.TraverseListener;
+import dwt.widgets.Control;
+import dwt.widgets.Display;
+import dwt.widgets.Item;
+import dwtx.core.runtime.ListenerList;
+
+import dwt.dwthelper.utils;
+
+/**
+ * This is the base for all editor implementations of Viewers. ColumnViewer
+ * implementators 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 int activationTime;
+
+    private ViewerCell cell;
+
+    private ColumnViewerEditorActivationEvent activationEvent;
+
+    private ListenerList editorActivationListener;
+
+    private ColumnViewerEditorActivationStrategy editorActivationStrategy;
+
+    /**
+     * 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;
+
+    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;
+        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();
+            }
+        };
+    }
+
+    void activateCellEditor() {
+
+        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) {
+                if (editorActivationListener !is null
+                        && !editorActivationListener.isEmpty()) {
+                    Object[] ls = editorActivationListener.getListeners();
+                    for (int i = 0; i < ls.length; i++) {
+
+                        if (activationEvent.cancel) {
+                            // Avoid leaking
+                            this.cell = null;
+                            return;
+                        }
+
+                        (cast(ColumnViewerEditorActivationListener) ls[i])
+                                .beforeEditorActivated(activationEvent);
+                    }
+                }
+
+                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;
+                }
+                setLayoutData(cellEditor.getLayoutData());
+                setEditor(control, cast(Item) cell.getItem(), cell.getColumnIndex());
+                cellEditor.setFocus();
+
+                if( cellEditor.dependsOnExternalFocusListener() ) {
+                    if (focusListener is null) {
+                        focusListener = new class FocusAdapter {
+                            public void focusLost(FocusEvent e) {
+                                applyEditorValue();
+                            }
+                        };
+                    }
+                    control.addFocusListener(focusListener);
+                }
+
+                mouseListener = new class MouseAdapter {
+                    Control control_;
+                    this(){
+                        control_=control;
+                    }
+                    public void mouseDown(MouseEvent e) {
+                        // time wrap?
+                        // check for expiration of doubleClickTime
+                        if (e.time <= activationTime) {
+                            control_.removeMouseListener(mouseListener);
+                            cancelEditing();
+                            handleDoubleClickEvent();
+                        } else if (mouseListener !is null) {
+                            control_.removeMouseListener(mouseListener);
+                        }
+                    }
+                };
+                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);
+                    }
+                }
+            }
+        } else {
+            // Avoid leaking
+            this.cell = null;
+        }
+    }
+
+    /**
+     * Applies the current value and deactivates the currently active cell
+     * editor.
+     */
+    void applyEditorValue() {
+        CellEditor c = this.cellEditor;
+        if (c !is null && this.cell !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 ?
+            ColumnViewerEditorDeactivationEvent tmp = new ColumnViewerEditorDeactivationEvent(
+                    cell);
+            if (editorActivationListener !is null
+                    && !editorActivationListener.isEmpty()) {
+                Object[] ls = editorActivationListener.getListeners();
+                for (int i = 0; i < ls.length; i++) {
+
+                    (cast(ColumnViewerEditorActivationListener) ls[i])
+                            .beforeEditorDeactivated(tmp);
+                }
+            }
+
+            this.cellEditor = null;
+            this.activationEvent = null;
+            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);
+            }
+
+            setEditor(null, null, 0);
+            c.removeListener(cellEditorListener);
+            Control control = c.getControl();
+            if (control !is null) {
+                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();
+
+            if (editorActivationListener !is null
+                    && !editorActivationListener.isEmpty()) {
+                Object[] ls = editorActivationListener.getListeners();
+                for (int i = 0; i < ls.length; i++) {
+                    (cast(ColumnViewerEditorActivationListener) ls[i])
+                            .afterEditorDeactivated(tmp);
+                }
+            }
+
+            this.cell = null;
+        }
+    }
+
+    /**
+     * Cancel editing
+     */
+    void cancelEditing() {
+        if (cellEditor !is null) {
+            ColumnViewerEditorDeactivationEvent tmp = new ColumnViewerEditorDeactivationEvent(
+                    cell);
+            if (editorActivationListener !is null
+                    && !editorActivationListener.isEmpty()) {
+                Object[] ls = editorActivationListener.getListeners();
+                for (int i = 0; i < ls.length; i++) {
+
+                    (cast(ColumnViewerEditorActivationListener) ls[i])
+                            .beforeEditorDeactivated(tmp);
+                }
+            }
+
+            setEditor(null, null, 0);
+            cellEditor.removeListener(cellEditorListener);
+
+            Control control = cellEditor.getControl();
+            if (control !is null) {
+                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();
+
+            if (editorActivationListener !is null
+                    && !editorActivationListener.isEmpty()) {
+                Object[] ls = editorActivationListener.getListeners();
+                for (int i = 0; i < ls.length; i++) {
+                    (cast(ColumnViewerEditorActivationListener) ls[i])
+                            .afterEditorDeactivated(tmp);
+                }
+            }
+
+            this.cellEditor = null;
+            this.activationEvent = null;
+            this.cell = null;
+        }
+    }
+
+    /**
+     * Enable the editor by mouse down
+     *
+     * @param event
+     */
+    void handleEditorActivationEvent(ColumnViewerEditorActivationEvent event) {
+        if (editorActivationStrategy.isEditorActivationEvent_package(event)) {
+            if (cellEditor !is null) {
+                applyEditorValue();
+            }
+
+            this.cell = cast(ViewerCell) event.getSource();
+
+            activationEvent = event;
+            activationTime = event.time
+                    + Display.getCurrent().getDoubleClickTime();
+
+            activateCellEditor();
+        }
+    }
+
+    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 DWT.TRAVERSE_TAB_PREVIOUS) {
+            event.doit = false;
+
+            if ((event.stateMask & DWT.CTRL) is DWT.CTRL
+                    && (feature & TABBING_VERTICAL) is TABBING_VERTICAL) {
+                cell2edit = searchCellAboveBelow(row, viewer, columnIndex, true);
+            } else if ((feature & TABBING_HORIZONTAL) is TABBING_HORIZONTAL) {
+                cell2edit = searchPreviousCell(row, viewer, columnIndex,
+                        columnIndex);
+            }
+        } else if (event.detail is DWT.TRAVERSE_TAB_NEXT) {
+            event.doit = false;
+
+            if ((event.stateMask & DWT.CTRL) is DWT.CTRL
+                    && (feature & TABBING_VERTICAL) is TABBING_VERTICAL) {
+                cell2edit = searchCellAboveBelow(row, viewer, columnIndex,
+                        false);
+            } else if ((feature & TABBING_HORIZONTAL) is TABBING_HORIZONTAL) {
+                cell2edit = searchNextCell(row, viewer, columnIndex,
+                        columnIndex);
+            }
+        }
+
+        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 ViewerCell searchPreviousCell(ViewerRow row, ColumnViewer viewer,
+            int columnIndex, int startIndex) {
+        ViewerCell rv = null;
+
+        if (columnIndex - 1 >= 0) {
+            ViewerColumn column = viewer.getViewerColumn(columnIndex - 1);
+            if (column !is null
+                    && column.getEditingSupport() !is null
+                    && column.getEditingSupport().canEdit_package(
+                            row.getItem().getData())) {
+                rv = row.getCell(columnIndex - 1);
+            } else {
+                rv = searchPreviousCell(row, viewer, columnIndex - 1,
+                        startIndex);
+            }
+        } else {
+            if ((feature & TABBING_CYCLE_IN_ROW) is TABBING_CYCLE_IN_ROW) {
+                // Check that we don't get into endless loop
+                if (columnIndex - 1 !is startIndex) {
+                    // Don't subtract -1 from getColumnCount() we need to
+                    // start in the virtual column
+                    // next to it
+                    rv = searchPreviousCell(row, viewer, row.getColumnCount(),
+                            startIndex);
+                }
+            } 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, viewer, rowAbove
+                            .getColumnCount(), startIndex);
+                }
+            }
+        }
+
+        return rv;
+    }
+
+    private ViewerCell searchNextCell(ViewerRow row, ColumnViewer viewer,
+            int columnIndex, int startIndex) {
+        ViewerCell rv = null;
+
+        if (columnIndex + 1 < row.getColumnCount()) {
+            ViewerColumn column = viewer.getViewerColumn(columnIndex + 1);
+            if (column !is null
+                    && column.getEditingSupport() !is null
+                    && column.getEditingSupport().canEdit_package(
+                            row.getItem().getData())) {
+                rv = row.getCell(columnIndex + 1);
+            } else {
+                rv = searchNextCell(row, viewer, columnIndex + 1, startIndex);
+            }
+        } else {
+            if ((feature & TABBING_CYCLE_IN_ROW) is TABBING_CYCLE_IN_ROW) {
+                // Check that we don't get into endless loop
+                if (columnIndex + 1 !is startIndex) {
+                    // Start from -1 from the virtual column before the
+                    // first one
+                    rv = searchNextCell(row, viewer, -1, startIndex);
+                }
+            } 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, viewer, -1, startIndex);
+                }
+            }
+        }
+
+        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(CellEditor.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/dwtx/jface/viewers/ColumnViewerEditorActivationEvent.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ ******************************************************************************/
+
+module dwtx.jface.viewers.ColumnViewerEditorActivationEvent;
+
+import dwtx.jface.viewers.ViewerCell;
+// import java.util.EventObject;
+
+import dwt.events.KeyEvent;
+import dwt.events.MouseEvent;
+import dwt.events.TraverseEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 = 0;
+        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/dwtx/jface/viewers/ColumnViewerEditorActivationListener.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ ******************************************************************************/
+
+module dwtx.jface.viewers.ColumnViewerEditorActivationListener;
+
+import dwtx.jface.viewers.ColumnViewerEditorActivationEvent;
+import dwtx.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/dwtx/jface/viewers/ColumnViewerEditorActivationStrategy.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.ColumnViewerEditorActivationStrategy;
+
+import dwtx.jface.viewers.ViewerCell;
+import dwtx.jface.viewers.ColumnViewer;
+import dwtx.jface.viewers.ColumnViewerEditorActivationEvent;
+import dwtx.jface.viewers.IStructuredSelection;
+
+import dwt.events.KeyEvent;
+import dwt.events.KeyListener;
+import dwt.events.MouseEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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/dwtx/jface/viewers/ColumnViewerEditorDeactivationEvent.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ColumnViewerEditorDeactivationEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * This event is fired when an editor deactivated
+ *
+ * @since 3.3
+ *
+ */
+public class ColumnViewerEditorDeactivationEvent : EventObject {
+
+    /**
+     *
+     */
+    private static const long serialVersionUID = 1L;
+
+    /**
+     * @param source
+     */
+    public this(Object source) {
+        super(source);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/ColumnViewerToolTipSupport.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * 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
+ *     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 dwtx.jface.viewers.ColumnViewerToolTipSupport;
+
+import dwtx.jface.viewers.ColumnViewer;
+import dwtx.jface.viewers.ViewerRow;
+import dwtx.jface.viewers.ViewerColumn;
+import dwtx.jface.viewers.CellLabelProvider;
+import dwtx.jface.viewers.StructuredSelection;
+
+import dwt.DWT;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+import dwt.widgets.Event;
+import dwtx.jface.util.Policy;
+import dwtx.jface.window.DefaultToolTip;
+import dwtx.jface.window.ToolTip;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 LABEL_PROVIDER_KEY = Policy.JFACE
+            ~ "_LABEL_PROVIDER"; //$NON-NLS-1$
+
+    private static const String ELEMENT_KEY = Policy.JFACE ~ "_ELEMENT_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 Object getToolTipArea(Event event) {
+        return viewer.getCell(new Point(event.x,event.y));
+    }
+
+    protected final 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();
+
+            ViewerColumn viewPart = viewer.getViewerColumn(row
+                    .getColumnIndex(point));
+
+            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(LABEL_PROVIDER_KEY, labelProvider);
+                setData(ELEMENT_KEY, element);
+
+                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 void afterHideToolTip(Event event) {
+        if (event !is null && event.widget !is viewer.getControl()) {
+            if (event.type is DWT.MouseDown) {
+                viewer.setSelection(new StructuredSelection());
+            } else {
+                viewer.getControl().setFocus();
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/ColumnWeightData.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ColumnWeightData;
+
+import dwtx.jface.viewers.ColumnLayoutData;
+
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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>
+ */
+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/dwtx/jface/viewers/ComboBoxCellEditor.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,297 @@
+/*******************************************************************************
+ * 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - bugfix in 199775
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.viewers.ComboBoxCellEditor;
+
+import dwtx.jface.viewers.CellEditor;
+
+import dwt.DWT;
+import dwt.custom.CCombo;
+import dwt.events.FocusAdapter;
+import dwt.events.FocusEvent;
+import dwt.events.KeyAdapter;
+import dwt.events.KeyEvent;
+import dwt.events.SelectionAdapter;
+import dwt.events.SelectionEvent;
+import dwt.events.TraverseEvent;
+import dwt.events.TraverseListener;
+import dwt.graphics.GC;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+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>
+ */
+public class ComboBoxCellEditor : CellEditor {
+
+    /**
+     * 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 = DWT.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 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 DWT.TRAVERSE_ESCAPE
+                        || e.detail is DWT.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 new ValueWrapperInt(selection);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on CellEditor.
+     */
+    protected 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 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 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 dwtx.jface.viewers.CellEditor#focusLost()
+     */
+    protected void focusLost() {
+        if (isActivated()) {
+            applyEditorValueAndDeactivate();
+        }
+    }
+
+    /*
+     *  (non-Javadoc)
+     * @see dwtx.jface.viewers.CellEditor#keyReleaseOccured(dwt.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/dwtx/jface/viewers/ComboViewer.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,241 @@
+/*******************************************************************************
+ * 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
+ *     Sebastian Davids - bug 69254
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.viewers.ComboViewer;
+
+import dwtx.jface.viewers.AbstractListViewer;
+
+import dwt.DWT;
+import dwt.custom.CCombo;
+import dwt.widgets.Combo;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A concrete viewer based either on an DWT <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 DWT combo control
+ * and configured with a domain-specific content provider, label provider, element
+ * filter (optional), and element sorter (optional).
+ * </p>
+ *
+ * @see dwtx.jface.viewers.ListViewer
+ * @since 3.0
+ */
+public final 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, DWT.READ_ONLY | DWT.BORDER);
+    }
+
+    /**
+     * Creates a combo viewer on a newly-created combo control under the given parent.
+     * The combo control is created using the given DWT 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 DWT 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 void listAdd(String string, int index) {
+        if (combo is null) {
+            ccombo.add(string, index);
+        } else {
+            combo.add(string, index);
+        }
+    }
+
+    protected void listSetItem(int index, String string) {
+        if (combo is null) {
+            ccombo.setItem(index, string);
+        } else {
+            combo.setItem(index, string);
+        }
+    }
+
+    protected int[] listGetSelectionIndices() {
+        if (combo is null) {
+            return [ ccombo.getSelectionIndex() ];
+        } else {
+            return [ combo.getSelectionIndex() ];
+        }
+    }
+
+    protected int listGetItemCount() {
+        if (combo is null) {
+            return ccombo.getItemCount();
+        } else {
+            return combo.getItemCount();
+        }
+    }
+
+    protected void listSetItems(String[] labels) {
+        if (combo is null) {
+            ccombo.setItems(labels);
+        } else {
+            combo.setItems(labels);
+        }
+    }
+
+    protected void listRemoveAll() {
+        if (combo is null) {
+            ccombo.removeAll();
+        } else {
+            combo.removeAll();
+        }
+    }
+
+    protected void listRemove(int index) {
+        if (combo is null) {
+            ccombo.remove(index);
+        } else {
+            combo.remove(index);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on Viewer.
+     */
+    public 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 void reveal(Object element) {
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.AbstractListViewer#listSetSelection(int[])
+     */
+    protected 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 dwtx.jface.viewers.AbstractListViewer#listDeselectAll()
+     */
+    protected void listDeselectAll() {
+        if (combo is null) {
+            ccombo.deselectAll();
+            ccombo.clearSelection();
+        } else {
+            combo.deselectAll();
+            combo.clearSelection();
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.AbstractListViewer#listShowSelection()
+     */
+    protected void listShowSelection() {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/ContentViewer.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,301 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.ContentViewer;
+
+import dwtx.jface.viewers.IBaseLabelProvider;
+import dwtx.jface.viewers.Viewer;
+import dwtx.jface.viewers.ILabelProviderListener;
+import dwtx.jface.viewers.IContentProvider;
+import dwtx.jface.viewers.LabelProviderChangedEvent;
+import dwtx.jface.viewers.LabelProvider;
+
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.widgets.Control;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 DWT controls for viewer (in constructor) (optional)
+ * </li>
+ * <li>
+ * initialize DWT 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 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 <code>SimpleLabelProvider</code>
+     * 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 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) {
+            oldProvider.dispose();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/CustomHashtable.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,443 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.CustomHashtable;
+
+import dwtx.jface.viewers.IElementComparer;
+// import java.util.Enumeration;
+// import java.util.NoSuchElementException;
+
+import dwt.dwthelper.utils;
+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 {
+
+    /**
+     * 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/dwtx/jface/viewers/DecoratingLabelProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,423 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.DecoratingLabelProvider;
+
+import dwtx.jface.viewers.LabelProvider;
+import dwtx.jface.viewers.ILabelProvider;
+import dwtx.jface.viewers.IViewerLabelProvider;
+import dwtx.jface.viewers.IColorProvider;
+import dwtx.jface.viewers.IFontProvider;
+import dwtx.jface.viewers.ITreePathLabelProvider;
+import dwtx.jface.viewers.ILabelDecorator;
+import dwtx.jface.viewers.IDecorationContext;
+import dwtx.jface.viewers.ILabelProviderListener;
+import dwtx.jface.viewers.ViewerLabel;
+import dwtx.jface.viewers.TreePath;
+import dwtx.jface.viewers.DecorationContext;
+import dwtx.jface.viewers.LabelDecorator;
+import dwtx.jface.viewers.LabelProviderChangedEvent;
+import dwtx.jface.viewers.IDelayedLabelDecorator;
+import dwtx.jface.viewers.IColorDecorator;
+import dwtx.jface.viewers.IFontDecorator;
+
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.Image;
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.ListenerList;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 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 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 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 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 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 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 dwtx.jface.viewers.IViewerLabelProvider#updateLabel(dwtx.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 dwtx.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 dwtx.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 dwtx.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 dwtx.jface.viewers.ITreePathLabelProvider#updateLabel(dwtx.jface.viewers.ViewerLabel, dwtx.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/dwtx/jface/viewers/DecorationContext.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.DecorationContext;
+
+import dwtx.jface.viewers.IDecorationContext;
+
+import tango.util.collection.HashMap;
+import tango.util.collection.model.Map;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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!(String,Object) properties;
+
+    /**
+     * Create a decoration context.
+     */
+    public this() {
+        properties = new HashMap!(String,Object);
+    }
+
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.IDecorationContext#getProperty(java.lang.String)
+     */
+    public Object getProperty(String property) {
+        return properties.get(property);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.IDecorationContext#getProperties()
+     */
+    public String[] getProperties() {
+        String[] res;
+        foreach( k,v; properties ){
+            res ~= k;
+        }
+        return res;
+    }
+
+    /**
+     * 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.removeKey(property);
+        } else {
+            properties.add(property, value);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/DecorationOverlayIcon.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,198 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.DecorationOverlayIcon;
+
+import dwtx.jface.viewers.IDecoration;
+import dwtx.jface.util.Util;
+
+// import tango.util.Arrays;
+
+import dwt.graphics.Image;
+import dwt.graphics.ImageData;
+import dwt.graphics.Point;
+import dwtx.jface.resource.CompositeImageDescriptor;
+import dwtx.jface.resource.ImageDescriptor;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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;
+            }
+        }
+    }
+
+    /* (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 = base.toHash();
+        for (int i = 0; i < overlays.length; i++) {
+            if (overlays[i] !is null) {
+                code ^= overlays[i].toHash();
+            }
+        }
+        return code;
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.resource.CompositeImageDescriptor#drawCompositeImage(int, int)
+     */
+    protected 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);
+            }
+        }
+        drawImage(base.getImageData(), 0, 0);
+        drawOverlays(overlays);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.resource.CompositeImageDescriptor#getSize()
+     */
+    protected Point getSize() {
+        return size;
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.resource.CompositeImageDescriptor#getTransparentPixel()
+     */
+    protected int getTransparentPixel() {
+        return base.getImageData().transparentPixel;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/DialogCellEditor.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,393 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.DialogCellEditor;
+
+import dwtx.jface.viewers.CellEditor;
+
+import dwt.DWT;
+import dwt.events.FocusEvent;
+import dwt.events.FocusListener;
+import dwt.events.KeyAdapter;
+import dwt.events.KeyEvent;
+import dwt.events.SelectionAdapter;
+import dwt.events.SelectionEvent;
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Button;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Label;
+import dwt.widgets.Layout;
+import dwtx.jface.resource.ImageDescriptor;
+import dwtx.jface.resource.ImageRegistry;
+import dwtx.jface.resource.JFaceResources;
+
+import dwt.dwthelper.utils;
+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(
+                DialogCellEditor.classinfo, "images/dots_button.gif"));//$NON-NLS-1$
+    }
+
+    /**
+     * Internal class for laying out the dialog.
+     */
+    private class DialogCellLayout : Layout {
+        public void layout(Composite editor, bool force) {
+            Rectangle bounds = editor.getClientArea();
+            Point size = button.computeSize(DWT.DEFAULT, DWT.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 Point computeSize(Composite editor, int wHint, int hHint,
+                bool force) {
+            if (wHint !is DWT.DEFAULT && hHint !is DWT.DEFAULT) {
+                return new Point(wHint, hHint);
+            }
+            Point contentsSize = contents.computeSize(DWT.DEFAULT, DWT.DEFAULT,
+                    force);
+            Point buttonSize = button.computeSize(DWT.DEFAULT, DWT.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 = DWT.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, DWT.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, DWT.LEFT);
+        defaultLabel.setFont(cell.getFont());
+        defaultLabel.setBackground(cell.getBackground());
+        return defaultLabel;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on CellEditor.
+     */
+    protected 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 dwt.events.KeyListener#keyReleased(dwt.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 dwt.events.SelectionListener#widgetSelected(dwt.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 dwtx.jface.viewers.CellEditor#deactivate()
+     */
+    public void deactivate() {
+        if (button !is null && !button.isDisposed()) {
+            button.removeFocusListener(getButtonFocusListener());
+        }
+
+        super.deactivate();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on CellEditor.
+     */
+    protected Object doGetValue() {
+        return value;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on CellEditor.
+     * The focus is set to the cell editor's button.
+     */
+    protected 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 dwt.events.FocusListener#focusGained(dwt.events.FocusEvent)
+                 */
+                public void focusGained(FocusEvent e) {
+                    // Do nothing
+                }
+
+                /* (non-Javadoc)
+                 * @see dwt.events.FocusListener#focusLost(dwt.events.FocusEvent)
+                 */
+                public void focusLost(FocusEvent e) {
+                    this.outer.focusLost();
+                }
+            };
+        }
+
+        return buttonFocusListener;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on CellEditor.
+     */
+    protected 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/dwtx/jface/viewers/DoubleClickEvent.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.DoubleClickEvent;
+
+import dwtx.jface.viewers.ISelection;
+import dwtx.jface.viewers.Viewer;
+
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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/dwtx/jface/viewers/EditingSupport.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * 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,200558
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.viewers.EditingSupport;
+
+import dwtx.jface.viewers.ColumnViewer;
+import dwtx.jface.viewers.CellEditor;
+import dwtx.jface.viewers.ViewerCell;
+
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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/dwtx/jface/viewers/FocusCellHighlighter.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ ******************************************************************************/
+
+module dwtx.jface.viewers.FocusCellHighlighter;
+
+import dwtx.jface.viewers.ColumnViewer;
+import dwtx.jface.viewers.ViewerCell;
+
+import dwt.dwthelper.utils;
+
+/**
+ * @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
+     */
+    protected void focusCellChanged(ViewerCell cell) {
+    }
+    package void focusCellChanged_package(ViewerCell cell){
+        focusCellChanged(cell);
+    }
+
+    /**
+     * 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/dwtx/jface/viewers/FocusCellOwnerDrawHighlighter.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * 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 183850, 182652
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ ******************************************************************************/
+
+module dwtx.jface.viewers.FocusCellOwnerDrawHighlighter;
+
+import dwtx.jface.viewers.FocusCellHighlighter;
+import dwtx.jface.viewers.ViewerCell;
+import dwtx.jface.viewers.ViewerRow;
+import dwtx.jface.viewers.ColumnViewer;
+
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.graphics.GC;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * @since 3.3
+ *
+ */
+public class FocusCellOwnerDrawHighlighter : FocusCellHighlighter {
+
+    private ViewerCell oldCell;
+
+    // Needed to work-around problem in bug 183850
+    private static const bool WIN_32;
+
+    static this(){
+        WIN_32 = "win32".equals(DWT.getPlatform()); //$NON-NLS-1$
+    }
+
+    /**
+     * @param viewer
+     *            the viewer
+     */
+    public this(ColumnViewer viewer) {
+        super(viewer);
+        hookListener(viewer);
+    }
+
+    private void markFocusedCell(Event event, ViewerCell cell) {
+        Color background = getSelectedCellBackgroundColor(cell);
+        Color foreground = getSelectedCellForegroundColor(cell);
+
+        if ( WIN_32 || foreground !is null || background !is null) {
+            GC gc = event.gc;
+
+            if (background is null) {
+                background = cell.getItem().getDisplay().getSystemColor(
+                        DWT.COLOR_LIST_SELECTION);
+            }
+
+            if (foreground is null) {
+                foreground = cell.getItem().getDisplay().getSystemColor(
+                        DWT.COLOR_LIST_SELECTION_TEXT);
+            }
+
+            gc.setBackground(background);
+            gc.setForeground(foreground);
+            gc.fillRectangle(event.getBounds());
+
+            // This is a workaround for an DWT-Bug on WinXP bug 169517
+            gc.drawText(" ", cell.getBounds().x, cell.getBounds().y, false); //$NON-NLS-1$
+            event.detail &= ~DWT.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());
+        // This is a workaround for an DWT-Bug on WinXP bug 169517
+        gc.drawText(" ", cell.getBounds().x, cell.getBounds().y, false); //$NON-NLS-1$
+        event.detail &= ~DWT.SELECTED;
+    }
+
+    private void hookListener(ColumnViewer viewer) {
+
+        Listener listener = new class Listener {
+            ColumnViewer viewer_;
+            this(){
+                viewer_ = viewer;
+            }
+            public void handleEvent(Event event) {
+                if ((event.detail & DWT.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(DWT.EraseItem, listener);
+    }
+
+    /**
+     * @param cell
+     *            the cell which is colored
+     * @return the color
+     */
+    protected Color getSelectedCellBackgroundColor(ViewerCell cell) {
+        return null;
+    }
+
+    /**
+     * @param cell
+     *            the cell which is colored
+     * @return the color
+     */
+    protected Color getSelectedCellForegroundColor(ViewerCell cell) {
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.FocusCellHighlighter#focusCellChanged(dwtx.jface.viewers.ViewerCell)
+     */
+    protected void focusCellChanged(ViewerCell cell) {
+        super.focusCellChanged(cell);
+
+        // Redraw new area
+        if (cell !is null) {
+            Rectangle rect = cell.getBounds();
+            int x = cell.getColumnIndex() is 0 ? 0 : rect.x;
+            int width = cell.getColumnIndex() is 0 ? rect.x + rect.width
+                    : rect.width;
+            // 1 is a fix for Linux-GTK
+            cell.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);
+        }
+
+        this.oldCell = cell;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/IBaseLabelProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.IBaseLabelProvider;
+
+import dwtx.jface.viewers.ILabelProviderListener;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 DWT 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/dwtx/jface/viewers/IBasicPropertyConstants.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.IBasicPropertyConstants;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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>"dwtx.jface.text"</code>)
+     * for an element's label text.
+     *
+     * @see dwtx.jface.viewers.ILabelProvider#getText
+     */
+    public static final String P_TEXT = "dwtx.jface.text"; //$NON-NLS-1$
+
+    /**
+     * Property name constant (value <code>"dwtx.jface.image"</code>)
+     * for an element's label image.
+     *
+     * @see dwtx.jface.viewers.ILabelProvider#getImage
+     */
+    public static final String P_IMAGE = "dwtx.jface.image"; //$NON-NLS-1$
+
+    /**
+     * Property name constant (value <code>"dwtx.jface.children"</code>)
+     * for an element's children.
+     */
+    public static final String P_CHILDREN = "dwtx.jface.children"; //$NON-NLS-1$
+
+    /**
+     * Property name constant (value <code>"dwtx.jface.parent"</code>)
+     * for an element's parent object.
+     */
+    public static final String P_PARENT = "dwtx.jface.parent"; //$NON-NLS-1$
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/ICellEditorListener.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.ICellEditorListener;
+
+/**
+ * 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/dwtx/jface/viewers/ICellEditorValidator.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ICellEditorValidator;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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/dwtx/jface/viewers/ICellModifier.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ICellModifier;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 DWT 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 DWT Item (see above)
+     * @param property the property
+     * @param value the new property value
+     *
+     * @see dwt.widgets.Item
+     */
+    public void modify(Object element, String property, Object value);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/ICheckStateListener.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ICheckStateListener;
+
+import dwtx.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/dwtx/jface/viewers/ICheckable.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.ICheckable;
+
+import dwtx.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/dwtx/jface/viewers/IColorDecorator.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.IColorDecorator;
+
+import dwt.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/dwtx/jface/viewers/IColorProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.IColorProvider;
+
+import dwt.graphics.Color;
+
+/**
+ * Interface to provide color representation for a given element.
+ * @see dwtx.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/dwtx/jface/viewers/IContentProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.IContentProvider;
+
+import dwtx.jface.viewers.Viewer;
+
+/**
+ * A content provider mediates between the viewer's model
+ * and the viewer itself.
+ *
+ * @see dwtx.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/dwtx/jface/viewers/IDecoration.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.IDecoration;
+
+import dwtx.jface.viewers.IDecorationContext;
+
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwtx.jface.resource.ImageDescriptor;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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>.
+ */
+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;
+
+    /**
+     * 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/dwtx/jface/viewers/IDecorationContext.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.IDecorationContext;
+
+import dwt.dwthelper.utils;
+
+
+/**
+ * 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/dwtx/jface/viewers/IDelayedLabelDecorator.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.IDelayedLabelDecorator;
+
+import dwtx.jface.viewers.ILabelDecorator;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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/dwtx/jface/viewers/IDoubleClickListener.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.IDoubleClickListener;
+
+import dwtx.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/dwtx/jface/viewers/IElementComparer.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.IElementComparer;
+
+/**
+ * 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/dwtx/jface/viewers/IFilter.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.IFilter;
+
+/**
+ * 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/dwtx/jface/viewers/IFontDecorator.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.IFontDecorator;
+
+import dwt.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/dwtx/jface/viewers/IFontProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.IFontProvider;
+
+import dwt.graphics.Font;
+
+/**
+ * Interface to provide font representation for a given element.
+ * @see dwtx.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/dwtx/jface/viewers/IInputProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.IInputProvider;
+
+/**
+ * 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/dwtx/jface/viewers/IInputSelectionProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.IInputSelectionProvider;
+
+import dwtx.jface.viewers.IInputProvider;
+import dwtx.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/dwtx/jface/viewers/ILabelDecorator.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ILabelDecorator;
+
+import dwtx.jface.viewers.IBaseLabelProvider;
+
+import dwt.graphics.Image;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 dwtx.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/dwtx/jface/viewers/ILabelProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ILabelProvider;
+
+import dwtx.jface.viewers.IBaseLabelProvider;
+
+import dwt.graphics.Image;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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/dwtx/jface/viewers/ILabelProviderListener.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ILabelProviderListener;
+
+import dwtx.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/dwtx/jface/viewers/ILazyContentProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.ILazyContentProvider;
+
+import dwtx.jface.viewers.IContentProvider;
+
+import dwt.dwthelper.utils;
+
+/**
+ * The ILazyContentProvider is the content provider
+ * for table viewers created using the DWT.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/dwtx/jface/viewers/ILazyTreeContentProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.ILazyTreeContentProvider;
+
+import dwtx.jface.viewers.IContentProvider;
+
+import dwt.dwthelper.utils;
+
+/**
+ * The ILazyTreeContentProvider is the content provider for tree viewers created
+ * using the DWT.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/dwtx/jface/viewers/ILazyTreePathContentProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.ILazyTreePathContentProvider;
+
+import dwtx.jface.viewers.IContentProvider;
+import dwtx.jface.viewers.TreePath;
+
+import dwt.dwthelper.utils;
+
+/**
+ * The ILazyTreePathContentProvider is a tree path-based content provider for
+ * tree viewers created using the DWT.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/dwtx/jface/viewers/ILightweightLabelDecorator.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ILightweightLabelDecorator;
+
+import dwtx.jface.viewers.IBaseLabelProvider;
+import dwtx.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/dwtx/jface/viewers/IOpenListener.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.IOpenListener;
+
+import dwtx.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/dwtx/jface/viewers/IPostSelectionProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.IPostSelectionProvider;
+
+import dwtx.jface.viewers.ISelectionProvider;
+import dwtx.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/dwtx/jface/viewers/ISelection.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.ISelection;
+
+/**
+ * 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/dwtx/jface/viewers/ISelectionChangedListener.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.ISelectionChangedListener;
+
+import dwtx.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/dwtx/jface/viewers/ISelectionProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ISelectionProvider;
+
+import dwtx.jface.viewers.ISelectionChangedListener;
+import dwtx.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/dwtx/jface/viewers/IStructuredContentProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.IStructuredContentProvider;
+
+import dwtx.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.
+     *
+     * @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/dwtx/jface/viewers/IStructuredSelection.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.IStructuredSelection;
+
+import dwtx.jface.viewers.ISelection;
+
+import tango.util.collection.model.SeqView;
+import tango.util.collection.model.Iterator;
+
+/**
+ * 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!(Object) 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>.
+     *
+     * @return the selected elements as a list
+     */
+    public SeqView!(Object) toList();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/ITableColorProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.ITableColorProvider;
+
+import dwt.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/dwtx/jface/viewers/ITableFontProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ITableFontProvider;
+
+import dwt.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/dwtx/jface/viewers/ITableLabelProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ITableLabelProvider;
+
+import dwtx.jface.viewers.IBaseLabelProvider;
+
+import dwt.graphics.Image;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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/dwtx/jface/viewers/ITreeContentProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.ITreeContentProvider;
+
+import dwtx.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/dwtx/jface/viewers/ITreePathContentProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ITreePathContentProvider;
+
+import dwtx.jface.viewers.IStructuredContentProvider;
+import dwtx.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/dwtx/jface/viewers/ITreePathLabelProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ITreePathLabelProvider;
+
+import dwtx.jface.viewers.IBaseLabelProvider;
+import dwtx.jface.viewers.ViewerLabel;
+import dwtx.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/dwtx/jface/viewers/ITreeSelection.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.ITreeSelection;
+
+import dwtx.jface.viewers.IStructuredSelection;
+import dwtx.jface.viewers.TreePath;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A selection containing tree paths.
+ *
+ * @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/dwtx/jface/viewers/ITreeViewerListener.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ITreeViewerListener;
+
+import dwtx.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/dwtx/jface/viewers/IViewerLabelProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.IViewerLabelProvider;
+
+import dwtx.jface.viewers.IBaseLabelProvider;
+import dwtx.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/dwtx/jface/viewers/LabelDecorator.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.LabelDecorator;
+
+import dwtx.jface.viewers.ILabelDecorator;
+import dwtx.jface.viewers.IDecorationContext;
+
+import dwt.graphics.Image;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 dwtx.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/dwtx/jface/viewers/LabelProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.LabelProvider;
+
+import dwtx.jface.viewers.BaseLabelProvider;
+import dwtx.jface.viewers.ILabelProvider;
+
+import dwt.graphics.Image;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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/dwtx/jface/viewers/LabelProviderChangedEvent.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.LabelProviderChangedEvent;
+
+import dwtx.jface.viewers.IBaseLabelProvider;
+// import java.util.EventObject;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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/dwtx/jface/viewers/ListViewer.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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
+ *     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 dwtx.jface.viewers.ListViewer;
+
+import dwtx.jface.viewers.AbstractListViewer;
+
+import tango.util.collection.model.SeqView;
+
+import dwt.DWT;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+static import dwt.widgets.List;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A concrete viewer based on an DWT <code>List</code> control.
+ * <p>
+ * This class is not intended to be subclassed. It is designed to be
+ * instantiated with a pre-existing DWT <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 DWT <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
+ */
+public class ListViewer : AbstractListViewer {
+
+    /**
+     * This viewer's list control.
+     */
+    private dwt.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 DWT 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, DWT.MULTI | DWT.H_SCROLL | DWT.V_SCROLL | DWT.BORDER);
+    }
+
+    /**
+     * Creates a list viewer on a newly-created list control under the given parent.
+     * The list control is created using the given DWT 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 DWT style bits
+     */
+    public this(Composite parent, int style) {
+        this(new dwt.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(dwt.widgets.List.List list) {
+        this.list = list;
+        hookControl(list);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on Viewer.
+     */
+    public Control getControl() {
+        return list;
+    }
+
+    /**
+     * Returns this list viewer's list control.
+     *
+     * @return the list control
+     */
+    public dwt.widgets.List.List getList() {
+        return list;
+    }
+
+    /*
+     * Non-Javadoc.
+     * Method defined on StructuredViewer.
+     */
+    public 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 dwtx.jface.viewers.AbstractListViewer#listAdd(java.lang.String, int)
+     */
+    protected void listAdd(String string, int index) {
+        list.add(string, index);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.AbstractListViewer#listSetItem(int, java.lang.String)
+     */
+    protected void listSetItem(int index, String string) {
+        list.setItem(index, string);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.AbstractListViewer#listGetSelectionIndices()
+     */
+    protected int[] listGetSelectionIndices() {
+        return list.getSelectionIndices();
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.AbstractListViewer#listGetItemCount()
+     */
+    protected int listGetItemCount() {
+        return list.getItemCount();
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.AbstractListViewer#listSetItems(java.lang.String[])
+     */
+    protected void listSetItems(String[] labels) {
+        list.setItems(labels);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.AbstractListViewer#listRemoveAll()
+     */
+    protected void listRemoveAll() {
+        list.removeAll();
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.AbstractListViewer#listRemove(int)
+     */
+    protected void listRemove(int index) {
+        list.remove(index);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.AbstractListViewer#listSelectAndShow(int[])
+     */
+    protected void listSetSelection(int[] ixs) {
+        list.setSelection(ixs);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.AbstractListViewer#listDeselectAll()
+     */
+    protected void listDeselectAll() {
+        list.deselectAll();
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.AbstractListViewer#listShowSelection()
+     */
+    protected void listShowSelection() {
+        list.showSelection();
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.AbstractListViewer#listGetTopIndex()
+     */
+    protected int listGetTopIndex() {
+        return list.getTopIndex();
+    }
+
+    /*
+     * (non-Javadoc)
+     * @see dwtx.jface.viewers.AbstractListViewer#listSetTopIndex(int)
+     */
+    protected void listSetTopIndex(int index) {
+        list.setTopIndex(index);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.AbstractListViewer#setSelectionToWidget(java.util.List, bool)
+     */
+    protected void setSelectionToWidget(SeqView!(Object) 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/dwtx/jface/viewers/NamedHandleObjectLabelProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.viewers.NamedHandleObjectLabelProvider;
+
+import dwtx.jface.viewers.DialogCellEditor;
+import dwtx.jface.viewers.LabelProvider;
+
+
+import dwtx.core.commands.common.NamedHandleObject;
+import dwtx.core.commands.common.NotDefinedException;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 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/dwtx/jface/viewers/OpenEvent.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.OpenEvent;
+
+import dwtx.jface.viewers.ISelection;
+import dwtx.jface.viewers.Viewer;
+
+// import java.util.EventObject;
+
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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/dwtx/jface/viewers/OwnerDrawLabelProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,211 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.OwnerDrawLabelProvider;
+
+import dwtx.jface.viewers.CellLabelProvider;
+import dwtx.jface.viewers.ColumnViewer;
+import dwtx.jface.viewers.ViewerCell;
+
+import dwt.DWT;
+import dwt.graphics.Color;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 {
+
+    /**
+     * Set up the owner draw callbacks for the viewer.
+     *
+     * @param viewer
+     *            the viewer the owner draw is set up
+     */
+    public static void setUpOwnerDraw(ColumnViewer viewer) {
+        viewer.getControl().addListener(DWT.MeasureItem, new class Listener {
+            ColumnViewer viewer_;
+            this(){
+                viewer_=viewer;
+            }
+            /*
+             * (non-Javadoc)
+             *
+             * @see dwt.widgets.Listener#handleEvent(dwt.widgets.Event)
+             */
+            public void handleEvent(Event event) {
+                CellLabelProvider provider = viewer_
+                        .getViewerColumn(event.index).getLabelProvider();
+                Object element = event.item.getData();
+
+                if ( auto p = cast(OwnerDrawLabelProvider) provider )
+                    p.measure(event, element);
+            }
+        });
+
+        viewer.getControl().addListener(DWT.PaintItem, new class Listener {
+            ColumnViewer viewer_;
+            this(){
+                viewer_=viewer;
+            }
+            /*
+             * (non-Javadoc)
+             *
+             * @see dwt.widgets.Listener#handleEvent(dwt.widgets.Event)
+             */
+            public void handleEvent(Event event) {
+                CellLabelProvider provider = viewer_
+                        .getViewerColumn(event.index).getLabelProvider();
+                Object element = event.item.getData();
+
+                if ( auto p = cast(OwnerDrawLabelProvider) provider )
+                    p.paint(event, element);
+            }
+        });
+
+        viewer.getControl().addListener(DWT.EraseItem, new class Listener {
+            ColumnViewer viewer_;
+            this(){
+                viewer_=viewer;
+            }
+            /*
+             * (non-Javadoc)
+             *
+             * @see dwt.widgets.Listener#handleEvent(dwt.widgets.Event)
+             */
+            public void handleEvent(Event event) {
+
+                CellLabelProvider provider = getLabelProvider(viewer_, event);
+                Object element = getElement(event);
+
+                if ( auto p = cast(OwnerDrawLabelProvider) provider )
+                    p.erase(event, element);
+
+            }
+
+            /**
+             * Return the item for the event
+             *
+             * @param event
+             * @return Object
+             */
+            private Object getElement(Event event) {
+                return event.item.getData();
+            }
+
+            /**
+             * Return the label provider for the column.
+             *
+             * @param viewer
+             * @param event
+             * @return CellLabelProvider
+             */
+            private CellLabelProvider getLabelProvider(
+                    ColumnViewer viewer, Event event) {
+                return viewer.getViewerColumn(event.index).getLabelProvider();
+            }
+        });
+    }
+
+    /**
+     * Handle the erase event. The default implementation colors the background
+     * of selected areas with {@link DWT#COLOR_LIST_SELECTION} and foregrounds
+     * with {@link DWT#COLOR_LIST_SELECTION_TEXT}
+     *
+     * @param event
+     *            the erase event
+     * @param element
+     *            the model object
+     * @see DWT#EraseItem
+     * @see DWT#COLOR_LIST_SELECTION
+     * @see DWT#COLOR_LIST_SELECTION_TEXT
+     */
+    protected void erase(Event event, Object element) {
+
+        Rectangle bounds = event.getBounds();
+        if ((event.detail & DWT.SELECTED) !is 0) {
+
+            Color oldForeground = event.gc.getForeground();
+            Color oldBackground = event.gc.getBackground();
+
+            event.gc.setBackground(event.item.getDisplay().getSystemColor(
+                    DWT.COLOR_LIST_SELECTION));
+            event.gc.setForeground(event.item.getDisplay().getSystemColor(
+                    DWT.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 &= ~DWT.SELECTED;
+
+        }
+
+    }
+
+    /**
+     * Handle the paint event.
+     *
+     * @param event
+     *            the paint event
+     * @param element
+     *            the model element
+     * @see DWT#PaintItem
+     */
+    protected abstract void paint(Event event, Object element);
+
+    /**
+     * Handle the measure event.
+     *
+     * @param event
+     *            the measure event
+     * @param element
+     *            the model element
+     * @see DWT#MeasureItem
+     */
+    protected abstract void measure(Event event, Object element);
+
+    /**
+     * Create a new instance of the receiver based on a column viewer.
+     *
+     */
+    public this() {
+
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.ViewerLabelProvider#update(dwtx.jface.viewers.ViewerCell)
+     */
+    public void update(ViewerCell cell) {
+        // Force a redraw
+        Rectangle cellBounds = cell.getBounds();
+        cell.getControl().redraw(cellBounds.x, cellBounds.y, cellBounds.width,
+                cellBounds.height, true);
+
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/SWTFocusCellManager.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,199 @@
+/*******************************************************************************
+ * 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
+ *                                               - bug fix for bug 187189
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ ******************************************************************************/
+
+module dwtx.jface.viewers.SWTFocusCellManager;
+
+import dwtx.jface.viewers.CellNavigationStrategy;
+import dwtx.jface.viewers.ColumnViewer;
+import dwtx.jface.viewers.ViewerCell;
+import dwtx.jface.viewers.ViewerRow;
+import dwtx.jface.viewers.FocusCellHighlighter;
+import dwtx.jface.viewers.Viewer;
+import dwtx.jface.viewers.ISelectionChangedListener;
+import dwtx.jface.viewers.SelectionChangedEvent;
+
+import dwt.DWT;
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.graphics.Point;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+import dwtx.core.runtime.Assert;
+
+/**
+ * This class is responsible to provide cell management base features for the
+ * DWT-Controls {@link dwt.widgets.Table} and
+ * {@link dwt.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 (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 DWT.MouseDown:
+                    handleMouseDown(event);
+                    break;
+                case DWT.KeyDown:
+                    handleKeyDown(event);
+                    break;
+                case DWT.Selection:
+                    handleSelection(event);
+                    break;
+                case DWT.FocusIn:
+                    handleFocusIn(event);
+                    break;
+                }
+            }
+        };
+
+        viewer.getControl().addListener(DWT.MouseDown, listener);
+        viewer.getControl().addListener(DWT.KeyDown, listener);
+        viewer.getControl().addListener(DWT.Selection, listener);
+        viewer.addSelectionChangedListener(new class ISelectionChangedListener {
+
+            public void selectionChanged(SelectionChangedEvent event) {
+                if( event.getSelection_package.isEmpty() ) {
+                    setFocusCell(null);
+                }
+            }
+
+        });
+        viewer.getControl().addListener(DWT.FocusIn, listener);
+    }
+
+    /**
+     * @return the cell with the focus
+     *
+     */
+    public ViewerCell getFocusCell() {
+        return focusCell;
+    }
+
+    void setFocusCell(ViewerCell 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);
+    }
+
+    ColumnViewer getViewer() {
+        return viewer;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/SelectionChangedEvent.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.SelectionChangedEvent;
+
+import dwtx.jface.viewers.ISelection;
+import dwtx.jface.viewers.ISelectionProvider;
+// import java.util.EventObject;
+
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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/dwtx/jface/viewers/StructuredSelection.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,191 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.StructuredSelection;
+
+import dwtx.jface.viewers.IStructuredSelection;
+
+// import java.util.Arrays;
+// import java.util.Iterator;
+import tango.util.collection.ArraySeq;
+import tango.util.collection.model.Seq;
+import tango.util.collection.model.SeqView;
+import tango.util.collection.model.Iterator;
+
+import dwtx.core.runtime.Assert;
+import dwtx.jface.resource.JFaceResources;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A concrete implementation of the <code>IStructuredSelection</code> interface,
+ * suitable for instantiating.
+ * <p>
+ * This class is not intended to be subclassed.
+ * </p>
+ */
+public class StructuredSelection : IStructuredSelection {
+
+    /**
+     * The element that make up this structured selection.
+     */
+    private Object[] elements;
+
+    /**
+     * 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(SeqView!(Object) elements) {
+        Assert.isNotNull(cast(Object)elements);
+        this.elements = elements.toArray();
+    }
+
+    /**
+     * 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 bool equals(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;
+        }
+
+        //size
+        int myLen = elements.length;
+        if (myLen !is s2.elements.length) {
+            return false;
+        }
+        //element comparison
+        for (int i = 0; i < myLen; i++) {
+            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!(Object) iterator() {
+        auto res = new ArraySeq!(Object);
+        res.capacity( elements.length );
+        foreach( o; elements ){
+            res.append( o );
+        }
+        return res.elements;
+    }
+
+    /* (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 SeqView!(Object) toList() {
+        auto res = new ArraySeq!(Object);
+        res.capacity( elements.length );
+        foreach( o; elements ){
+            res.append( o );
+        }
+        return res;
+    }
+
+    /**
+     * Internal method which returns a string representation of this
+     * selection suitable for debug purposes only.
+     *
+     * @return debug string
+     */
+    public 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/dwtx/jface/viewers/StructuredViewer.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,2278 @@
+/*******************************************************************************
+ * 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
+ *     Tom Schindl - bug 151205
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.StructuredViewer;
+
+import dwtx.jface.viewers.IBaseLabelProvider;
+import dwtx.jface.viewers.IPostSelectionProvider;
+import dwtx.jface.viewers.ContentViewer;
+import dwtx.jface.viewers.CustomHashtable;
+import dwtx.jface.viewers.IElementComparer;
+import dwtx.jface.viewers.ViewerComparator;
+import dwtx.jface.viewers.ViewerFilter;
+import dwtx.jface.viewers.IDoubleClickListener;
+import dwtx.jface.viewers.IOpenListener;
+import dwtx.jface.viewers.ISelectionChangedListener;
+import dwtx.jface.viewers.OpenEvent;
+import dwtx.jface.viewers.SelectionChangedEvent;
+import dwtx.jface.viewers.ISelection;
+import dwtx.jface.viewers.DoubleClickEvent;
+import dwtx.jface.viewers.ViewerSorter;
+import dwtx.jface.viewers.IContentProvider;
+import dwtx.jface.viewers.IColorProvider;
+import dwtx.jface.viewers.IFontProvider;
+import dwtx.jface.viewers.LabelProviderChangedEvent;
+import dwtx.jface.viewers.ViewerLabel;
+import dwtx.jface.viewers.TreePath;
+import dwtx.jface.viewers.ITreePathLabelProvider;
+import dwtx.jface.viewers.IViewerLabelProvider;
+import dwtx.jface.viewers.ILabelProvider;
+import dwtx.jface.viewers.IStructuredContentProvider;
+import dwtx.jface.viewers.StructuredSelection;
+import dwtx.jface.viewers.IStructuredSelection;
+
+import tango.util.collection.ArraySeq;
+import tango.util.collection.model.Seq;
+import tango.util.collection.model.SeqView;
+
+import dwt.custom.TableTreeItem;
+import dwt.dnd.DragSource;
+import dwt.dnd.DragSourceListener;
+import dwt.dnd.DropTarget;
+import dwt.dnd.DropTargetListener;
+import dwt.dnd.Transfer;
+import dwt.events.DisposeEvent;
+import dwt.events.SelectionAdapter;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.widgets.Control;
+import dwt.widgets.Item;
+import dwt.widgets.TableItem;
+import dwt.widgets.TreeItem;
+import dwt.widgets.Widget;
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.ListenerList;
+import dwtx.jface.util.IOpenEventListener;
+import dwtx.jface.util.OpenStrategy;
+import dwtx.jface.util.SafeRunnable;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+
+/**
+ * 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.setSelection setSelection;
+    /**
+     * A map from the viewer's model elements to DWT 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 Seq!(ViewerFilter) 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 dwtx.jface.viewers.StructuredViewer.ColorAndFontManager#setFontsAndColors(java.lang.Object)
+         */
+        public 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 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 dwtx.jface.viewers.StructuredViewer.ColorAndFontManager#applyFontsAndColors(dwt.widgets.TreeItem)
+         */
+        public 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 dwtx.jface.viewers.StructuredViewer.ColorAndFontManager#applyFontsAndColors(dwt.custom.TableTreeItem)
+         */
+        public 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 ColorAndFontManager 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 dwt.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 dwt.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 ArraySeq!(ViewerFilter);
+        }
+        filters.append(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 DWT 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 DWT 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) {
+            ArraySeq!(Object) filtered = new ArraySeq!(Object);
+            filtered.capacity(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.append(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(new class SafeRunnable {
+                IDoubleClickListener l;
+                this(){
+                    l = cast(IDoubleClickListener) listeners[i];
+                }
+                public void run() {
+                    l.doubleClick(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(new class SafeRunnable {
+                IOpenListener l;
+                this(){
+                    l = cast(IOpenListener) listeners[i];
+                }
+                public void run() {
+                    l.open(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(new class SafeRunnable {
+                ISelectionChangedListener l;
+                this(){
+                    l = cast(ISelectionChangedListener) listeners[i];
+                }
+                public void run() {
+                    l.selectionChanged(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) {
+            foreach (f;filters) {
+                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 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 DWT-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(dwt.graphics.Point)}.
+     * Viewers who are not subclasses of {@link ColumnViewer} should consider using a
+     * widget relative implementation like {@link ColumnViewer#getItemAt(dwt.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 ISelection getSelection() {
+        Control control = getControl();
+        if (control is null || control.isDisposed()) {
+            return StructuredSelection.EMPTY;
+        }
+        auto list = getSelectionFromWidget();
+        return new StructuredSelection(list);
+    }
+
+    /**
+     * Retrieves the selection, as a <code>List</code>, from the underlying
+     * widget.
+     *
+     * @return the list of selected elements
+     */
+    protected abstract SeqView!(Object) getSelectionFromWidget();
+    package SeqView!(Object) 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 DWT 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 DWT 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 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 DWT 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 DWT selection event
+     */
+    protected void handlePostSelect(SelectionEvent e) {
+        SelectionChangedEvent event = new SelectionChangedEvent(this, getSelection());
+        firePostSelectionChanged(event);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Viewer.
+     */
+    protected 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 ArrayWrapperObject([ cast(Object)
+                            w, item ]));
+                }
+            } else {
+                Widget[] widgets = (cast(ArrayWrapperT!(Widget)) widgetOrWidgets).array;
+                int indexOfItem = -1;
+                foreach( idx, w; widgets ){
+                    if( w == item ){
+                        indexOfItem = idx;
+                        break;
+                    }
+                }
+                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) {
+            foreach( filter; filters ){
+                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 selection did not
+     * take</li>
+     * <li>calls <code>postUpdateHook</code></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>
+     * <li>calls <code>postUpdateHook</code></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 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(new class Runnable {
+            Object element_;
+            this(){
+                element_ = element;
+            }
+            public void run() {
+                internalRefresh(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(new class Runnable {
+            Object element_;
+            bool updateLabels_;
+            this(){
+                element_ = element;
+                updateLabels_ = updateLabels;
+            }
+            public void run() {
+                internalRefresh(element_, updateLabels_);
+            }
+        });
+    }
+
+    /**
+     *
+     * Refreshes the given TableItem 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.
+            int delIdx = 0;
+            foreach( o; filters ){
+                if (o is filter) {
+                    filters.removeAt(delIdx);
+                    refresh();
+                    if (filters.size() is 0) {
+                        filters = null;
+                    }
+                    return;
+                }
+                delIdx++;
+            }
+        }
+    }
+
+    /**
+     * 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 ArraySeq!(ViewerFilter);
+            foreach( f; filters ){
+                this.filters.append(f);
+            }
+            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 dwtx.jface.viewers.ContentViewer#setContentProvider(dwtx.jface.viewers.IContentProvider)
+     */
+    public 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 dwtx.jface.viewers.Viewer#setInput(java.lang.Object)
+     */
+    public final void setInput(Object input) {
+
+        try {
+            //      fInChange= true;
+
+            unmapAllElements();
+
+            super.setInput(input);
+
+        } finally {
+            //      fInChange= false;
+        }
+    }
+
+    /*
+     *  (non-Javadoc)
+     * @see dwtx.jface.viewers.Viewer#setSelection(dwtx.jface.viewers.ISelection, bool)
+     */
+    public 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(SeqView!(Object) 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(SeqView!(Object)) 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 DWT 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 = -1;
+                foreach( idx, w; widgets ){
+                    if( w == item ){
+                        indexOfItem = idx;
+                        break;
+                    }
+                }
+                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));
+                }
+            }
+        }
+    }
+
+    /**
+     * 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) {
+        for (int i = 0; i < elements.length; ++i) {
+            update(elements[i], properties);
+        }
+    }
+
+    /**
+     * 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);
+
+        for (int i = 0; i < items.length; i++) {
+            internalUpdate(items[i], element, properties);
+        }
+    }
+
+    /**
+     * 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(new class Runnable {
+                public void run() {
+                    internalRefresh(getRoot());
+                }
+            });
+            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 DWT 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 dwtx.jface.viewers.ContentViewer#setLabelProvider(dwtx.jface.viewers.IBaseLabelProvider)
+     */
+    public 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 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/dwtx/jface/viewers/TableColumnViewerLabelProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.TableColumnViewerLabelProvider;
+
+import dwtx.jface.viewers.WrappedViewerLabelProvider;
+import dwtx.jface.viewers.ITableLabelProvider;
+import dwtx.jface.viewers.ITableColorProvider;
+import dwtx.jface.viewers.ITableFontProvider;
+import dwtx.jface.viewers.IBaseLabelProvider;
+import dwtx.jface.viewers.ViewerCell;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 dwtx.jface.viewers.WrappedViewerLabelProvider#update(dwtx.jface.viewers.ViewerCell)
+     */
+    public 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/dwtx/jface/viewers/TableLayout.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,243 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.TableLayout;
+
+import dwtx.jface.viewers.ColumnLayoutData;
+import dwtx.jface.viewers.ColumnPixelData;
+import dwtx.jface.viewers.ColumnWeightData;
+
+import tango.util.collection.model.Seq;
+import tango.util.collection.ArraySeq;
+
+import dwt.DWT;
+import dwt.graphics.Point;
+import dwt.widgets.Composite;
+import dwt.widgets.Item;
+import dwt.widgets.Layout;
+import dwt.widgets.Table;
+import dwt.widgets.TableColumn;
+import dwt.widgets.Tree;
+import dwt.widgets.TreeColumn;
+import dwtx.core.runtime.Assert;
+import dwtx.jface.layout.TableColumnLayout;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 const int COLUMN_TRIM;
+    static this(){
+        COLUMN_TRIM = "carbon".equals(DWT.getPlatform()) ? 24 : 3; //$NON-NLS-1$
+    }
+
+    /**
+     * The list of column layout data (element type:
+     * <code>ColumnLayoutData</code>).
+     */
+    private Seq!(ColumnLayoutData) columns;
+
+    /**
+     * Indicates whether <code>layout</code> has yet to be called.
+     */
+    private bool firstTime = true;
+
+    /**
+     * Creates a new table layout.
+     */
+    public this() {
+        columns = new ArraySeq!(ColumnLayoutData);
+    }
+
+    /**
+     * Adds a new column of data to this table layout.
+     *
+     * @param data
+     *            the column layout data
+     */
+    public void addColumnData(ColumnLayoutData data) {
+        columns.append(data);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Layout.
+     */
+    public Point computeSize(Composite c, int wHint, int hHint, bool flush) {
+        if (wHint !is DWT.DEFAULT && hHint !is DWT.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();
+        foreach( layoutData; columns ){
+            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 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/dwtx/jface/viewers/TableTreeViewer.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,882 @@
+/*******************************************************************************
+ * 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
+ *     Tom Schindl <tom.schindl@bestsolution.at> - bug 153993
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.viewers.TableTreeViewer;
+
+import dwtx.jface.viewers.AbstractTreeViewer;
+import dwtx.jface.viewers.CellEditor;
+import dwtx.jface.viewers.ICellEditorListener;
+import dwtx.jface.viewers.ICellModifier;
+import dwtx.jface.viewers.ColumnViewer;
+import dwtx.jface.viewers.IBaseLabelProvider;
+import dwtx.jface.viewers.StructuredSelection;
+import dwtx.jface.viewers.Viewer;
+import dwtx.jface.viewers.DoubleClickEvent;
+import dwtx.jface.viewers.OpenEvent;
+import dwtx.jface.viewers.ITableLabelProvider;
+import dwtx.jface.viewers.ViewerLabel;
+
+import tango.util.collection.model.SeqView;
+
+import dwt.DWT;
+import dwt.custom.TableTree;
+import dwt.custom.TableTreeEditor;
+import dwt.custom.TableTreeItem;
+import dwt.events.FocusAdapter;
+import dwt.events.FocusEvent;
+import dwt.events.FocusListener;
+import dwt.events.MouseAdapter;
+import dwt.events.MouseEvent;
+import dwt.events.MouseListener;
+import dwt.events.TreeListener;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Display;
+import dwt.widgets.Item;
+import dwt.widgets.Widget;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A concrete viewer based on a DWT <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 DWT 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
+ */
+public class TableTreeViewer : AbstractTreeViewer {
+    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 MouseAdapter {
+                            Control control_;
+                            this(){
+                                control_=control;
+                            }
+                            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() {
+            Object[] res;
+            foreach( str; columnProperties ){
+                res ~= new ArrayWrapperString( str );
+            }
+            return res;
+        }
+
+        /**
+         * 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(CellEditor.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 DWT 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, DWT.MULTI | DWT.H_SCROLL | DWT.V_SCROLL | DWT.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 DWT
+     * 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 DWT style bits
+     */
+    public this(Composite parent, int style) {
+        this(new TableTree(parent, style));
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected 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 void cancelEditing() {
+        tableEditorImpl.cancelEditing();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected 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 void editElement(Object element, int column) {
+        tableEditorImpl.editElement(element, column);
+    }
+
+    /**
+     * Returns the cell editors of this viewer.
+     *
+     * @return the list of cell editors
+     */
+    public CellEditor[] getCellEditors() {
+        return tableEditorImpl.getCellEditors();
+    }
+
+    /**
+     * Returns the cell modifier of this viewer.
+     *
+     * @return the cell modifier
+     */
+    public ICellModifier getCellModifier() {
+        return tableEditorImpl.getCellModifier();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected 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 dwtx.jface.viewers.AbstractTreeViewer#getChild(dwt.widgets.Widget,
+     *      int)
+     */
+    protected 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 Object[] getColumnProperties() {
+        return tableEditorImpl.getColumnProperties();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on Viewer.
+     */
+    public 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: DWT: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 bool getExpanded(Item item) {
+        return (cast(TableTreeItem) item).getExpanded();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.ColumnViewer#getItemAt(dwt.graphics.Point)
+     */
+    protected Item getItemAt(Point p) {
+        return getTableTree().getTable().getItem(p);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected int getItemCount(Control widget) {
+        return (cast(TableTree) widget).getItemCount();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected int getItemCount(Item item) {
+        return (cast(TableTreeItem) item).getItemCount();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected dwt.widgets.Item.Item[] getItems(
+            dwt.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 IBaseLabelProvider getLabelProvider() {
+        return super.getLabelProvider();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected Item getParentItem(Item item) {
+        return (cast(TableTreeItem) item).getParentItem();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on AbstractTreeViewer.
+     */
+    protected 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 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. DWT should either have
+                // support to see
+                // if the user clicked on the [+]/[-] or manage the table editor
+                // activation
+                dwt.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 bool isCellEditorActive() {
+        return tableEditorImpl.isCellEditorActive();
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected 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 void removeAll(Control widget) {
+        (cast(TableTree) widget).removeAll();
+    }
+
+    /**
+     * Sets the cell editors of this table viewer.
+     *
+     * @param editors
+     *            the list of cell editors
+     */
+    public void setCellEditors(CellEditor[] editors) {
+        tableEditorImpl.setCellEditors(editors);
+    }
+
+    /**
+     * Sets the cell modifier of this table viewer.
+     *
+     * @param modifier
+     *            the cell modifier
+     */
+    public 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 void setColumnProperties(String[] columnProperties) {
+        tableEditorImpl.setColumnProperties(columnProperties);
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected void setExpanded(Item node, bool expand) {
+        (cast(TableTreeItem) node).setExpanded(expand);
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected void setSelection(SeqView!(Item) items) {
+        getTableTree().setSelection(cast(TableTreeItem[]) items.toArray);
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected void showItem(Item item) {
+        getTableTree().showItem(cast(TableTreeItem) item);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/TableViewer.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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
+ *     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 dwtx.jface.viewers.TableViewer;
+
+import dwtx.jface.viewers.AbstractTableViewer;
+import dwtx.jface.viewers.TableViewerRow;
+import dwtx.jface.viewers.ColumnViewerEditor;
+import dwtx.jface.viewers.ISelection;
+import dwtx.jface.viewers.ViewerRow;
+import dwtx.jface.viewers.TableViewerEditor;
+import dwtx.jface.viewers.ColumnViewerEditorActivationStrategy;
+
+import dwt.DWT;
+import dwt.graphics.Point;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Item;
+import dwt.widgets.Table;
+import dwt.widgets.TableItem;
+import dwt.widgets.Widget;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+
+/**
+ * A concrete viewer based on a DWT <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 DWT 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 DWT.VIRTUAL flag. If the
+ * underlying table is DWT.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 DWT.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 DWT.VIRTUAL.
+ * </p>
+ *
+ * @see DWT#VIRTUAL
+ * @see #doFindItem(Object)
+ * @see #internalRefresh(Object, bool)
+ */
+public class TableViewer : AbstractTableViewer {
+
+    public alias AbstractTableViewer.preservingSelection preservingSelection;
+
+    /**
+     * 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 DWT 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, DWT.MULTI | DWT.H_SCROLL | DWT.V_SCROLL | DWT.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
+     *            DWT 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 Control getControl() {
+        return table;
+    }
+
+    /**
+     * Returns this table viewer's table control.
+     *
+     * @return the table control
+     */
+    public Table getTable() {
+        return table;
+    }
+
+    protected 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 void setSelection(ISelection selection, bool reveal) {
+        super.setSelection(selection, reveal);
+    }
+
+    protected 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 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 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 int doGetItemCount() {
+        return table.getItemCount();
+    }
+
+    protected int doIndexOf(Item item) {
+        return table.indexOf(cast(TableItem)item);
+    }
+
+    protected void doSetItemCount(int count) {
+        table.setItemCount(count);
+    }
+
+    protected Item[] doGetItems() {
+        return table.getItems();
+    }
+
+    protected int doGetColumnCount() {
+        return table.getColumnCount();
+    }
+
+    protected Widget doGetColumn(int index) {
+        return table.getColumn(index);
+    }
+
+    protected Item doGetItem(int index) {
+        return table.getItem(index);
+    }
+
+    protected Item[] doGetSelection() {
+        return table.getSelection();
+    }
+
+    protected int[] doGetSelectionIndices() {
+        return table.getSelectionIndices();
+    }
+
+    protected void doClearAll() {
+        table.clearAll();
+    }
+
+    protected 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 void doRemove(int start, int end) {
+        table.remove(start, end);
+    }
+
+    protected void doRemoveAll() {
+        table.removeAll();
+    }
+
+    protected void doRemove(int[] indices) {
+        table.remove(indices);
+    }
+
+    protected void doShowItem(Item item) {
+        table.showItem(cast(TableItem)item);
+    }
+
+    protected void doDeselectAll() {
+        table.deselectAll();
+    }
+
+    protected 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 void doShowSelection() {
+        table.showSelection();
+    }
+
+    protected void doSetSelection(int[] indices) {
+        table.setSelection(indices);
+    }
+
+    protected void doClear(int index) {
+        table.clear(index);
+    }
+
+    protected 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 (isBusy())
+            return;
+
+        if( isCellEditorActive() ) {
+            cancelEditing();
+        }
+
+        preservingSelection(new class Runnable {
+            Object element_;
+            bool updateLabels_;
+            this(){
+                element_ = element;
+                updateLabels_ = updateLabels;
+            }
+            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/dwtx/jface/viewers/TableViewerColumn.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.TableViewerColumn;
+
+import dwtx.jface.viewers.ViewerColumn;
+import dwtx.jface.viewers.TableViewer;
+
+import dwt.widgets.Table;
+import dwt.widgets.TableColumn;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 DWT table column
+     */
+    public TableColumn getColumn() {
+        return column;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/TableViewerEditor.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,161 @@
+/*******************************************************************************
+ * 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
+ *                                                 fixes in bug 198665
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ ******************************************************************************/
+
+module dwtx.jface.viewers.TableViewerEditor;
+
+import dwtx.jface.viewers.ColumnViewerEditor;
+import dwtx.jface.viewers.SWTFocusCellManager;
+import dwtx.jface.viewers.CellEditor;
+import dwtx.jface.viewers.TableViewer;
+import dwtx.jface.viewers.ColumnViewerEditorActivationStrategy;
+import dwtx.jface.viewers.ColumnViewerEditorActivationEvent;
+import dwtx.jface.viewers.ViewerCell;
+import dwtx.jface.viewers.StructuredSelection;
+
+// import tango.util.collection.model.Seq;
+
+import dwt.custom.TableEditor;
+import dwt.widgets.Control;
+import dwt.widgets.Item;
+import dwt.widgets.Table;
+import dwt.widgets.TableItem;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 void setEditor(Control w, Item item, int columnNumber) {
+        tableEditor.setEditor(w, cast(TableItem) item, columnNumber);
+    }
+
+    protected void setLayoutData(CellEditor.LayoutData layoutData) {
+        tableEditor.grabHorizontal = layoutData.grabHorizontal;
+        tableEditor.horizontalAlignment = layoutData.horizontalAlignment;
+        tableEditor.minimumWidth = layoutData.minimumWidth;
+    }
+
+    public ViewerCell getFocusCell() {
+        if (focusCellManager !is null) {
+            return focusCellManager.getFocusCell();
+        }
+
+        return super.getFocusCell();
+    }
+
+    protected 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/dwtx/jface/viewers/TableViewerFocusCellManager.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ ******************************************************************************/
+
+module dwtx.jface.viewers.TableViewerFocusCellManager;
+
+import dwtx.jface.viewers.SWTFocusCellManager;
+import dwtx.jface.viewers.CellNavigationStrategy;
+import dwtx.jface.viewers.TableViewer;
+import dwtx.jface.viewers.FocusCellHighlighter;
+import dwtx.jface.viewers.ViewerCell;
+
+import dwt.widgets.Table;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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
+     *
+     * @param viewer
+     *            the viewer the manager is bound to
+     * @param focusDrawingDelegate
+     *            the delegate responsible to highlight selected cell
+     */
+    public this(TableViewer viewer,
+            FocusCellHighlighter focusDrawingDelegate) {
+        super(viewer, focusDrawingDelegate, TABLE_NAVIGATE);
+    }
+
+    ViewerCell getInitialFocusCell() {
+        Table table = cast(Table) getViewer().getControl();
+
+        if (table.getItemCount() > 0) {
+            return getViewer().getViewerRowFromItem_package(table.getItem(0))
+                    .getCell(0);
+        }
+
+        return null;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/TableViewerRow.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,205 @@
+/*******************************************************************************
+ * 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
+ *                                              - Fix for bug 174355
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.viewers.TableViewerRow;
+
+import dwtx.jface.viewers.ViewerRow;
+import dwtx.jface.viewers.TreePath;
+
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.Image;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Control;
+import dwt.widgets.TableItem;
+import dwt.widgets.Widget;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 dwtx.jface.viewers.ViewerRow#getBounds(int)
+     */
+    public Rectangle getBounds(int columnIndex) {
+        return item.getBounds(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getBounds()
+     */
+    public Rectangle getBounds() {
+        return item.getBounds();
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getItem()
+     */
+    public Widget getItem() {
+        return item;
+    }
+
+    void setItem(TableItem item) {
+        this.item = item;
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getColumnCount()
+     */
+    public int getColumnCount() {
+        return item.getParent().getColumnCount();
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getBackground(int)
+     */
+    public Color getBackground(int columnIndex) {
+        return item.getBackground(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getFont(int)
+     */
+    public Font getFont(int columnIndex) {
+        return item.getFont(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getForeground(int)
+     */
+    public Color getForeground(int columnIndex) {
+        return item.getForeground(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getImage(int)
+     */
+    public Image getImage(int columnIndex) {
+        return item.getImage(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getText(int)
+     */
+    public String getText(int columnIndex) {
+        return item.getText(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#setBackground(int, dwt.graphics.Color)
+     */
+    public void setBackground(int columnIndex, Color color) {
+        item.setBackground(columnIndex, color);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#setFont(int, dwt.graphics.Font)
+     */
+    public void setFont(int columnIndex, Font font) {
+        item.setFont(columnIndex, font);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#setForeground(int, dwt.graphics.Color)
+     */
+    public void setForeground(int columnIndex, Color color) {
+        item.setForeground(columnIndex, color);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#setImage(int, dwt.graphics.Image)
+     */
+    public void setImage(int columnIndex, Image image) {
+        Image oldImage = item.getImage(columnIndex);
+        if (oldImage !is image) {
+            item.setImage(columnIndex,image);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#setText(int, java.lang.String)
+     */
+    public void setText(int columnIndex, String text) {
+        item.setText(columnIndex, text is null ? "" : text); //$NON-NLS-1$
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getControl()
+     */
+    public Control getControl() {
+        return item.getParent();
+    }
+
+    public 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 TreePath getTreePath() {
+        return new TreePath([item.getData()]);
+    }
+
+    public Object clone() {
+        return new TableViewerRow(item);
+    }
+
+    public Object getElement() {
+        return item.getData();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/TextCellEditor.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,485 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.viewers.TextCellEditor;
+
+import dwtx.jface.viewers.CellEditor;
+
+import dwt.DWT;
+import dwt.events.FocusAdapter;
+import dwt.events.FocusEvent;
+import dwt.events.KeyAdapter;
+import dwt.events.KeyEvent;
+import dwt.events.ModifyEvent;
+import dwt.events.ModifyListener;
+import dwt.events.MouseAdapter;
+import dwt.events.MouseEvent;
+import dwt.events.SelectionAdapter;
+import dwt.events.SelectionEvent;
+import dwt.events.TraverseEvent;
+import dwt.events.TraverseListener;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Text;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+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>
+ */
+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 = DWT.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 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 DWT.TRAVERSE_ESCAPE
+                        || e.detail is DWT.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 Object doGetValue() {
+        return new ArrayWrapperString(text.getText());
+    }
+
+    /* (non-Javadoc)
+     * Method declared on CellEditor.
+     */
+    protected 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 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 DWT 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 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 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 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 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 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 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 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() & DWT.MULTI) !is 0) {
+                if ((keyEvent.stateMask & DWT.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 void performCopy() {
+        text.copy();
+    }
+
+    /**
+     * The <code>TextCellEditor</code> implementation of this
+     * <code>CellEditor</code> method cuts the
+     * current selection to the clipboard.
+     */
+    public 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 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 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 void performSelectAll() {
+        text.selectAll();
+        checkSelection();
+        checkDeleteable();
+    }
+
+    bool dependsOnExternalFocusListener() {
+        return this.classinfo !is TextCellEditor.classinfo;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/TreeColumnViewerLabelProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.TreeColumnViewerLabelProvider;
+
+import dwtx.jface.viewers.IBaseLabelProvider;
+import dwtx.jface.viewers.TableColumnViewerLabelProvider;
+import dwtx.jface.viewers.ITreePathLabelProvider;
+import dwtx.jface.viewers.ViewerLabel;
+import dwtx.jface.viewers.TreePath;
+import dwtx.jface.viewers.ILabelProviderListener;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 dwtx.jface.viewers.ITreePathLabelProvider#updateLabel(dwtx.jface.viewers.ViewerLabel,
+            *      dwtx.jface.viewers.TreePath)
+            */
+            public void updateLabel(ViewerLabel label, TreePath elementPath) {
+                // Do nothing by default
+
+            }
+
+            /*
+            * (non-Javadoc)
+            *
+            * @see dwtx.jface.viewers.IBaseLabelProvider#dispose()
+            */
+            public void dispose() {
+                // Do nothing by default
+
+            }
+
+            /*
+            * (non-Javadoc)
+            *
+            * @see dwtx.jface.viewers.IBaseLabelProvider#addListener(dwtx.jface.viewers.ILabelProviderListener)
+            */
+            public void addListener(ILabelProviderListener listener) {
+                // Do nothing by default
+
+            }
+
+            /*
+            * (non-Javadoc)
+            *
+            * @see dwtx.jface.viewers.IBaseLabelProvider#removeListener(dwtx.jface.viewers.ILabelProviderListener)
+            */
+            public void removeListener(ILabelProviderListener listener) {
+                // Do nothing by default
+
+            }
+
+            /* (non-Javadoc)
+            * @see dwtx.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 dwtx.jface.viewers.ViewerLabelProvider#setProviders(java.lang.Object)
+     */
+    public 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/dwtx/jface/viewers/TreeExpansionEvent.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.TreeExpansionEvent;
+
+import dwtx.jface.viewers.AbstractTreeViewer;
+
+import dwt.dwthelper.utils;//.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/dwtx/jface/viewers/TreeNode.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,139 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ ******************************************************************************/
+
+module dwtx.jface.viewers.TreeNode;
+
+import dwtx.jface.util.Util;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A simple data structure that is useful for implemented tree models. This can
+ * be returned by
+ * {@link dwtx.jface.viewers.IStructuredContentProvider#getElements(Object)}.
+ * It allows simple delegation of methods from
+ * {@link dwtx.jface.viewers.ITreeContentProvider} such as
+ * {@link dwtx.jface.viewers.ITreeContentProvider#getChildren(Object)},
+ * {@link dwtx.jface.viewers.ITreeContentProvider#getParent(Object)} and
+ * {@link dwtx.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/dwtx/jface/viewers/TreeNodeContentProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ ******************************************************************************/
+
+module dwtx.jface.viewers.TreeNodeContentProvider;
+
+import dwtx.jface.viewers.ITreeContentProvider;
+import dwtx.jface.viewers.Viewer;
+import dwtx.jface.viewers.TreeNode;
+
+import dwt.dwthelper.utils;
+
+/**
+ * <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 dwtx.jface.viewers.TreeNode
+ */
+public class TreeNodeContentProvider : ITreeContentProvider {
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.IContentProvider#dispose()
+     */
+    public void dispose() {
+        // Do nothing
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
+     */
+    public Object[] getChildren(Object parentElement) {
+        TreeNode node = cast(TreeNode) parentElement;
+        return node.getChildren();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.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 dwtx.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
+     */
+    public Object getParent(Object element) {
+        TreeNode node = cast(TreeNode) element;
+        return node.getParent();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
+     */
+    public bool hasChildren(Object element) {
+        TreeNode node = cast(TreeNode) element;
+        return node.hasChildren();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.IContentProvider#inputChanged(dwtx.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/dwtx/jface/viewers/TreePath.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.TreePath;
+
+import dwtx.jface.viewers.IElementComparer;
+
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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/dwtx/jface/viewers/TreePathViewerSorter.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.TreePathViewerSorter;
+
+import dwtx.jface.viewers.ViewerSorter;
+import dwtx.jface.viewers.TreePath;
+import dwtx.jface.viewers.Viewer;
+
+// import java.util.Arrays;
+// import java.util.Comparator;
+
+import dwt.dwthelper.utils;
+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 {
+    public alias ViewerSorter.category category;
+    public alias ViewerSorter.compare compare;
+    public alias ViewerSorter.isSorterProperty isSorterProperty;
+
+    /**
+     * 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/dwtx/jface/viewers/TreeSelection.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,229 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.TreeSelection;
+
+import dwtx.jface.viewers.StructuredSelection;
+import dwtx.jface.viewers.ITreeSelection;
+import dwtx.jface.viewers.CustomHashtable;
+import dwtx.jface.viewers.TreePath;
+import dwtx.jface.viewers.IElementComparer;
+
+import tango.util.collection.ArraySeq;
+import tango.util.collection.model.Seq;
+
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 {
+        Seq!(Object) 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;
+            auto s = new ArraySeq!(Object);
+            s.capacity(size);
+            selection = s;
+            for (int i = 0; i < size; i++) {
+                Object lastSegment= paths[i].getLastSegment();
+                Object mapped= element2TreePaths.get(lastSegment);
+                if (mapped is null) {
+                    selection.append(lastSegment);
+                    element2TreePaths.put(lastSegment, paths[i]);
+                } else if ( cast(Seq!(Object))mapped ) {
+                    (cast(Seq!(Object))mapped).append(paths[i]);
+                } else {
+                    Seq!(Object) newMapped= new ArraySeq!(Object);
+                    newMapped.append(mapped);
+                    newMapped.append(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.equals(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(Seq!(Object))value ) {
+            auto l= cast(Seq!(Object))value;
+            return cast(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/dwtx/jface/viewers/TreeViewer.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,1164 @@
+/*******************************************************************************
+ * 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.schindl@bestsolution.at> - concept of ViewerRow,
+ *                                                 refactoring (bug 153993), bug 167323, 191468
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.viewers.TreeViewer;
+
+import dwtx.jface.viewers.AbstractTreeViewer;
+import dwtx.jface.viewers.TreeViewerRow;
+import dwtx.jface.viewers.IBaseLabelProvider;
+import dwtx.jface.viewers.ColumnViewerEditor;
+import dwtx.jface.viewers.IContentProvider;
+import dwtx.jface.viewers.TreeSelection;
+import dwtx.jface.viewers.ViewerRow;
+import dwtx.jface.viewers.ISelection;
+import dwtx.jface.viewers.TreeViewerEditor;
+import dwtx.jface.viewers.ColumnViewerEditorActivationStrategy;
+import dwtx.jface.viewers.ColumnViewerEditorActivationEvent;
+import dwtx.jface.viewers.ILazyTreeContentProvider;
+import dwtx.jface.viewers.ILazyTreePathContentProvider;
+import dwtx.jface.viewers.TreePath;
+import dwtx.jface.viewers.TreeExpansionEvent;
+import dwtx.jface.viewers.ViewerCell;
+
+import tango.util.collection.LinkSeq;
+import tango.util.collection.model.Seq;
+import tango.util.collection.model.SeqView;
+
+import dwt.DWT;
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.events.TreeEvent;
+import dwt.events.TreeListener;
+import dwt.graphics.Point;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Event;
+import dwt.widgets.Item;
+import dwt.widgets.Listener;
+import dwt.widgets.Tree;
+import dwt.widgets.TreeItem;
+import dwt.widgets.Widget;
+import dwtx.jface.util.Policy;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+
+/**
+ * A concrete viewer based on an DWT <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 DWT 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 DWT#VIRTUAL} style bit, and the tree viewer will not
+ * support sorting or filtering.
+ * </p>
+ */
+public class TreeViewer : AbstractTreeViewer {
+
+    public alias AbstractTreeViewer.preservingSelection preservingSelection;
+    public alias AbstractTreeViewer.getSelection getSelection;
+    public 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 DWT 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, DWT.MULTI | DWT.H_SCROLL | DWT.V_SCROLL | DWT.BORDER);
+    }
+
+    /**
+     * Creates a tree viewer on a newly-created tree control under the given
+     * parent. The tree control is created using the given DWT 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 DWT 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 void addTreeListener(Control c, TreeListener listener) {
+        (cast(Tree) c).addTreeListener(listener);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.ColumnViewer#getColumnViewerOwner(int)
+     */
+    protected 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 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 Control getControl() {
+        return tree;
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected bool getExpanded(Item item) {
+        return (cast(TreeItem) item).getExpanded();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.ColumnViewer#getItemAt(dwt.graphics.Point)
+     */
+    protected 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 int getItemCount(Control widget) {
+        return (cast(Tree) widget).getItemCount();
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected int getItemCount(Item item) {
+        return (cast(TreeItem) item).getItemCount();
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected 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 IBaseLabelProvider getLabelProvider() {
+        return super.getLabelProvider();
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected Item getParentItem(Item item) {
+        return (cast(TreeItem) item).getParentItem();
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected 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 dwtx.jface.viewers.AbstractTreeViewer#hookControl(dwt.widgets.Control)
+     */
+    protected void hookControl(Control control) {
+        super.hookControl(control);
+        Tree treeControl = cast(Tree) control;
+
+        if ((treeControl.getStyle() & DWT.VIRTUAL) !is 0) {
+            treeControl.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent e) {
+                    treeIsDisposed = true;
+                    unmapAllElements();
+                }
+            });
+            treeControl.addListener(DWT.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 ColumnViewerEditor createViewerEditor() {
+        return new TreeViewerEditor(this,null,new ColumnViewerEditorActivationStrategy(this),ColumnViewerEditor.DEFAULT);
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected 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 void removeAll(Control widget) {
+        (cast(Tree) widget).removeAll();
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected 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 void setSelection(SeqView!(Item) items) {
+
+        Item[] current = getSelection(getTree());
+
+        // Don't bother resetting the same selection
+        if (isSameSelection(items, current)) {
+            return;
+        }
+
+        getTree().setSelection( cast(TreeItem[]) items.toArray());
+    }
+
+    /*
+     * (non-Javadoc) Method declared in AbstractTreeViewer.
+     */
+    protected void showItem(Item item) {
+        getTree().showItem(cast(TreeItem) item);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.AbstractTreeViewer#getChild(dwt.widgets.Widget,
+     *      int)
+     */
+    protected 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 void assertContentProviderType(IContentProvider provider) {
+        if ( null !is cast(ILazyTreeContentProvider)provider
+                || null !is cast(ILazyTreePathContentProvider)provider ) {
+            return;
+        }
+        super.assertContentProviderType(provider);
+    }
+
+    protected Object[] getRawChildren(Object parent) {
+        if (contentProviderIsLazy) {
+            return new Object[0];
+        }
+        return super.getRawChildren(parent);
+    }
+
+    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 (isBusy())
+            return;
+        preservingSelection(new class Runnable {
+            Object elementOrTreePath_;
+            int count_;
+            this(){
+                elementOrTreePath_=elementOrTreePath;
+                count_=count;
+            }
+            public void run() {
+                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_);
+                }
+            }
+        });
+    }
+
+    /**
+     * 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 (isBusy())
+            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 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 Object getParentElement(Object element) {
+        bool oldBusy = busy;
+        busy = 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 {
+            busy = oldBusy;
+        }
+    }
+
+    protected 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 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 dwtx.jface.viewers.AbstractTreeViewer#internalRefreshStruct(dwt.widgets.Widget,
+     *      java.lang.Object, bool)
+     */
+    protected 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 void mapElement(Object element, Widget item) {
+        super.mapElement(element, item);
+        // make sure to unmap elements if the tree is virtual
+        if ((getTree().getStyle() & DWT.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, new ValueWrapperBool(true));
+                item.addDisposeListener(new class DisposeListener {
+                    Widget item_;
+                    this(){
+                        item_=item;
+                    }
+                    public void widgetDisposed(DisposeEvent e) {
+                        if (!treeIsDisposed) {
+                            Object data = item_.getData();
+                            if (usingElementMap() && data !is null) {
+                                unmapElement(data, item_);
+                            }
+                        }
+                    }
+                });
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.ColumnViewer#getRowPartFromItem(dwt.widgets.Widget)
+     */
+    protected 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(),
+                    DWT.NONE, rowIndex));
+        }
+
+        return getViewerRowFromItem(new TreeItem(cast(TreeItem) parent.getItem(),
+                DWT.NONE));
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.AbstractTreeViewer#internalInitializeTree(dwt.widgets.Control)
+     */
+    protected void internalInitializeTree(Control widget) {
+        if (contentProviderIsLazy) {
+            if (cast(Tree)widget && widget.getData() !is null) {
+                virtualLazyUpdateChildCount(widget, 0);
+                return;
+            }
+        }
+        super.internalInitializeTree(tree);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.AbstractTreeViewer#updatePlus(dwt.widgets.Item,
+     *      java.lang.Object)
+     */
+    protected 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 (isBusy())
+            return;
+        preservingSelection(new class Runnable {
+            Seq!(TreePath) oldSelection;
+            Object parentOrTreePath;
+            int index;
+            this(){
+                parentOrTreePath=parentOrTreePath_;
+                index=index_;
+                oldSelection = new LinkSeq!(TreePath);
+                foreach( p; (cast(TreeSelection) getSelection()).getPaths()){
+                    oldSelection.append( p );
+                }
+            }
+            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 (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;
+                    int delIdx = 0;
+                    foreach ( path; oldSelection.dup ) {
+                        if (path.startsWith(removedPath, getComparer())) {
+                            oldSelection.removeAt(delIdx);
+                            removed = true;
+                        }
+                        delIdx++;
+                    }
+                    if (removed) {
+                        setSelection(new TreeSelection(
+                                oldSelection.toArray(), getComparer()),
+                                false);
+                    }
+
+                }
+            }
+        });
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.AbstractTreeViewer#handleTreeExpand(dwt.events.TreeEvent)
+     */
+    protected 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 dwtx.jface.viewers.AbstractTreeViewer#setContentProvider(dwtx.jface.viewers.IContentProvider)
+     */
+    public 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 (isBusy())
+            return;
+        preservingSelection(new class Runnable {
+            Object elementOrTreePath;
+            bool hasChildren;
+            this(){
+                elementOrTreePath=elementOrTreePath_;
+                hasChildren=hasChildren_;
+            }
+            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 = busy;
+        busy = 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 {
+            busy = oldBusy;
+        }
+    }
+
+    /**
+     * Update the child count
+     * @param widget
+     * @param currentChildCount
+     */
+    private void virtualLazyUpdateChildCount(Widget widget, int currentChildCount) {
+        bool oldBusy = busy;
+        busy = 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 {
+            busy = oldBusy;
+        }
+    }
+
+    /**
+     * Update the item with the current child count.
+     * @param item
+     * @param currentChildCount
+     */
+    private void virtualLazyUpdateHasChildren(Item item, int currentChildCount) {
+        bool oldBusy = busy;
+        busy = 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 {
+            busy = oldBusy;
+        }
+    }
+
+    protected void disassociate(Item item) {
+        if (contentProviderIsLazy) {
+            // avoid causing a callback:
+            item.setText(" "); //$NON-NLS-1$
+        }
+        super.disassociate(item);
+    }
+
+    protected 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 void setSelection(ISelection selection, bool reveal) {
+        super.setSelection(selection, reveal);
+    }
+
+    public void editElement(Object element, int column) {
+        if( cast(TreePath)element ) {
+            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) {
+                        getControl().setRedraw(false);
+                        try {
+                            triggerEditorActivationEvent(new ColumnViewerEditorActivationEvent(cell));
+                        } finally {
+                            getControl().setRedraw(true);
+                        }
+                    }
+                }
+            }
+        } else {
+            super.editElement(element, column);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/TreeViewerColumn.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.TreeViewerColumn;
+
+import dwtx.jface.viewers.ViewerColumn;
+import dwtx.jface.viewers.TreeViewer;
+
+import dwt.widgets.Tree;
+import dwt.widgets.TreeColumn;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 DWT column
+     */
+    public TreeColumn getColumn() {
+        return column;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/TreeViewerEditor.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * 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
+ *                                                 fixes in bug 198665
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ ******************************************************************************/
+
+module dwtx.jface.viewers.TreeViewerEditor;
+
+import dwtx.jface.viewers.ColumnViewerEditor;
+import dwtx.jface.viewers.SWTFocusCellManager;
+import dwtx.jface.viewers.CellEditor;
+import dwtx.jface.viewers.TreeViewer;
+import dwtx.jface.viewers.ColumnViewerEditorActivationEvent;
+import dwtx.jface.viewers.ColumnViewerEditorActivationStrategy;
+import dwtx.jface.viewers.ViewerCell;
+import dwtx.jface.viewers.TreeSelection;
+
+import tango.util.collection.model.Seq;
+
+import dwt.custom.TreeEditor;
+import dwt.widgets.Control;
+import dwt.widgets.Item;
+import dwt.widgets.Tree;
+import dwt.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 void setEditor(Control w, Item item, int fColumnNumber) {
+        treeEditor.setEditor(w, cast(TreeItem) item, fColumnNumber);
+    }
+
+    protected void setLayoutData(CellEditor.LayoutData layoutData) {
+        treeEditor.grabHorizontal = layoutData.grabHorizontal;
+        treeEditor.horizontalAlignment = layoutData.horizontalAlignment;
+        treeEditor.minimumWidth = layoutData.minimumWidth;
+    }
+
+    public ViewerCell getFocusCell() {
+        if (focusCellManager !is null) {
+            return focusCellManager.getFocusCell();
+        }
+
+        return super.getFocusCell();
+    }
+
+    protected 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/dwtx/jface/viewers/TreeViewerFocusCellManager.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ ******************************************************************************/
+
+module dwtx.jface.viewers.TreeViewerFocusCellManager;
+
+import dwtx.jface.viewers.SWTFocusCellManager;
+import dwtx.jface.viewers.CellNavigationStrategy;
+import dwtx.jface.viewers.TreeViewer;
+import dwtx.jface.viewers.FocusCellHighlighter;
+import dwtx.jface.viewers.ViewerCell;
+import dwtx.jface.viewers.ColumnViewer;
+
+import dwt.DWT;
+import dwt.widgets.Event;
+import dwt.widgets.Item;
+import dwt.widgets.Tree;
+import dwt.widgets.TreeItem;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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) {
+                return cellToCollapse !is null
+                        && (cast(TreeItem) cellToCollapse.getItem()).getExpanded()
+                        && cellToCollapse.getColumnIndex() is 0
+                        && event.keyCode is DWT.ARROW_LEFT;
+            }
+
+            public bool isExpandEvent(ColumnViewer viewer,
+                    ViewerCell cellToExpand, Event event) {
+                return cellToExpand !is null
+                        && (cast(TreeItem) cellToExpand.getItem()).getItemCount() > 0
+                        && !(cast(TreeItem) cellToExpand.getItem()).getExpanded()
+                        && cellToExpand.getColumnIndex() is 0
+                        && event.keyCode is DWT.ARROW_RIGHT;
+            }
+        };
+    }
+    /**
+     * Create a new manager
+     *
+     * @param viewer
+     *            the viewer the manager is bound to
+     * @param focusDrawingDelegate
+     *            the delegate responsible to highlight selected cell
+     */
+    public this(TreeViewer viewer,
+            FocusCellHighlighter focusDrawingDelegate) {
+        super(viewer, focusDrawingDelegate,TREE_NAVIGATE);
+    }
+
+    ViewerCell getInitialFocusCell() {
+        Tree tree = cast(Tree) getViewer().getControl();
+
+        if( tree.getItemCount() > 0 ) {
+            return getViewer().getViewerRowFromItem_package(tree.getItem(0)).getCell(0);
+        }
+
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/TreeViewerRow.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,321 @@
+/*******************************************************************************
+ * 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 174355, 171126
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.viewers.TreeViewerRow;
+
+import dwtx.jface.viewers.ViewerRow;
+import dwtx.jface.viewers.TreePath;
+
+import tango.util.collection.LinkSeq;
+
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.Image;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Control;
+import dwt.widgets.Tree;
+import dwt.widgets.TreeItem;
+import dwt.widgets.Widget;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 dwtx.jface.viewers.ViewerRow#getBounds(int)
+     */
+    public Rectangle getBounds(int columnIndex) {
+        return item.getBounds(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getBounds()
+     */
+    public Rectangle getBounds() {
+        return item.getBounds();
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getColumnCount()
+     */
+    public int getColumnCount() {
+        return item.getParent().getColumnCount();
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getItem()
+     */
+    public Widget getItem() {
+        return item;
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getBackground(int)
+     */
+    public Color getBackground(int columnIndex) {
+        return item.getBackground(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getFont(int)
+     */
+    public Font getFont(int columnIndex) {
+        return item.getFont(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getForeground(int)
+     */
+    public Color getForeground(int columnIndex) {
+        return item.getForeground(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getImage(int)
+     */
+    public Image getImage(int columnIndex) {
+        return item.getImage(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getText(int)
+     */
+    public String getText(int columnIndex) {
+        return item.getText(columnIndex);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#setBackground(int, dwt.graphics.Color)
+     */
+    public void setBackground(int columnIndex, Color color) {
+        item.setBackground(columnIndex, color);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#setFont(int, dwt.graphics.Font)
+     */
+    public void setFont(int columnIndex, Font font) {
+        item.setFont(columnIndex, font);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#setForeground(int, dwt.graphics.Color)
+     */
+    public void setForeground(int columnIndex, Color color) {
+        item.setForeground(columnIndex, color);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#setImage(int, dwt.graphics.Image)
+     */
+    public void setImage(int columnIndex, Image image) {
+        Image oldImage = item.getImage(columnIndex);
+        if (image !is oldImage) {
+            item.setImage(columnIndex, image);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#setText(int, java.lang.String)
+     */
+    public void setText(int columnIndex, String text) {
+        item.setText(columnIndex, text is null ? "" : text); //$NON-NLS-1$
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ViewerRow#getControl()
+     */
+    public Control getControl() {
+        return item.getParent();
+    }
+
+
+    public 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 TreePath getTreePath() {
+        TreeItem tItem = item;
+        auto segments = new LinkSeq!(Object);
+        while (tItem !is null) {
+            Object segment = tItem.getData();
+            Assert.isNotNull(segment);
+            segments.prepend(segment);
+            tItem = tItem.getParentItem();
+        }
+
+        return new TreePath(segments.toArray());
+    }
+
+    void setItem(TreeItem item) {
+        this.item = item;
+    }
+
+    public Object clone() {
+        return new TreeViewerRow(item);
+    }
+
+    public Object getElement() {
+        return item.getData();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/Viewer.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,422 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.Viewer;
+
+import dwtx.jface.viewers.IInputSelectionProvider;
+import dwtx.jface.viewers.ISelectionChangedListener;
+import dwtx.jface.viewers.SelectionChangedEvent;
+import dwtx.jface.viewers.ISelection;
+
+import dwt.events.HelpEvent;
+import dwt.events.HelpListener;
+import dwt.widgets.Control;
+import dwt.widgets.Item;
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.ListenerList;
+import dwtx.jface.util.SafeRunnable;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 DWT controls for viewer (in constructor) (optional)
+ * </li>
+ * <li>
+ * initialize DWT 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>dwt.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 dwt.widgets.Widget#setData(String, Object)
+     */
+    protected static const String WIDGET_DATA_KEY = "dwtx.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(dwt.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 SafeRunnable {
+                ISelectionChangedListener l;
+                SelectionChangedEvent event_;
+                this(){
+                    event_=event;
+                    l = cast(ISelectionChangedListener) listeners[i];
+                }
+                public void run() {
+                    l.selectionChanged(event_);
+                }
+            });
+        }
+    }
+
+    /**
+     * Returns the primary control associated with this viewer.
+     *
+     * @return the DWT 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 DWT 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/dwtx/jface/viewers/ViewerCell.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,294 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.ViewerCell;
+
+import dwtx.jface.viewers.ViewerRow;
+
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.Image;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Control;
+import dwt.widgets.Item;
+import dwt.widgets.Widget;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 int ABOVE = 1;
+
+    /**
+     * Constant denoting the cell below current one (value is 2).
+     */
+    public static int BELOW = 1 << 1;
+
+    /**
+     * Constant denoting the cell to the left of the current one (value is 4).
+     */
+    public static int LEFT = 1 << 2;
+
+    /**
+     * Constant denoting the cell to the right of the current one (value is 8).
+     */
+    public static 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;
+        }
+        return row.getElement();
+    }
+
+    /**
+     * 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 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();
+    }
+
+    /**
+     * 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;
+        int columnIndex;
+
+        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) {
+            if ((directionMask & LEFT) is LEFT) {
+                columnIndex = getColumnIndex() - 1;
+            } else if ((directionMask & RIGHT) is RIGHT) {
+                columnIndex = getColumnIndex() + 1;
+            } else {
+                columnIndex = getColumnIndex();
+            }
+
+            if (columnIndex >= 0 && columnIndex < row.getColumnCount()) {
+                return row.getCell(columnIndex);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * @return the row
+     */
+    public ViewerRow getViewerRow() {
+        return row;
+    }
+
+    /* (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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/ViewerColumn.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * 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
+ *                                                fix for bug 163317,200558
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.viewers.ViewerColumn;
+
+import dwtx.jface.viewers.CellLabelProvider;
+import dwtx.jface.viewers.EditingSupport;
+import dwtx.jface.viewers.ILabelProviderListener;
+import dwtx.jface.viewers.ColumnViewer;
+import dwtx.jface.viewers.ViewerCell;
+import dwtx.jface.viewers.LabelProviderChangedEvent;
+
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.widgets.Widget;
+import dwtx.jface.util.Policy;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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;
+
+    /**
+     * 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) {
+        columnOwner.setData(ViewerColumn.COLUMN_VIEWER_KEY, this);
+        this.listener = new class ILabelProviderListener {
+            ColumnViewer viewer_;
+            this(){
+                viewer_= viewer;
+            }
+            public void labelProviderChanged(LabelProviderChangedEvent event) {
+                viewer_.handleLabelProviderChanged_package(event);
+            }
+
+        };
+        columnOwner.addDisposeListener(new class DisposeListener {
+            ColumnViewer viewer_;
+            this(){
+                viewer_= viewer;
+            }
+            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;
+        }
+
+        this.labelProvider = labelProvider;
+
+        if (registerListener) {
+            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.
+     *
+     * @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();
+        }
+        editingSupport = null;
+        listener = null;
+    }
+
+    private void handleDispose(ColumnViewer viewer) {
+        handleDispose();
+        viewer.clearLegacyEditingSetup();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/ViewerComparator.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,199 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.ViewerComparator;
+
+import dwtx.jface.viewers.Viewer;
+import dwtx.jface.viewers.ContentViewer;
+import dwtx.jface.viewers.IBaseLabelProvider;
+import dwtx.jface.viewers.ILabelProvider;
+
+// import java.util.Arrays;
+// import java.util.Comparator;
+
+import dwtx.jface.util.Policy;
+
+import dwt.dwthelper.utils;
+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);
+            }
+        );
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/ViewerDropAdapter.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,463 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.ViewerDropAdapter;
+
+import dwtx.jface.viewers.Viewer;
+import dwtx.jface.viewers.IStructuredSelection;
+import dwtx.jface.viewers.ISelection;
+
+import dwt.dnd.DND;
+import dwt.dnd.DropTargetAdapter;
+import dwt.dnd.DropTargetEvent;
+import dwt.dnd.TransferData;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Item;
+import dwt.widgets.TableItem;
+import dwt.widgets.TreeItem;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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
+     * and expanding on or off. Default is <code>true</code>.
+     */
+    private bool scrollExpandEnabled = 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 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 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 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 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 void dropAccept(DropTargetEvent event) {
+        if (!validateDrop(currentTarget, event.detail, event.currentDataType)) {
+            event.detail = DND.DROP_NONE;
+        }
+    }
+
+    /**
+     * Returns the bounds of the given DWT tree or table item.
+     *
+     * @param item the DWT 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 DWT
+        // 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 (scrollExpandEnabled) {
+            event.feedback |= DND.FEEDBACK_EXPAND | 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) {
+        scrollExpandEnabled = 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/dwtx/jface/viewers/ViewerFilter.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.ViewerFilter;
+
+import dwtx.jface.viewers.Viewer;
+import dwtx.jface.viewers.TreePath;
+
+import tango.util.collection.ArraySeq;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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;
+        auto out_ = new ArraySeq!(Object);
+        out_.capacity(size);
+        for (int i = 0; i < size; ++i) {
+            Object element = elements[i];
+            if (select(viewer, parent, element)) {
+                out_.append(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/dwtx/jface/viewers/ViewerLabel.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,418 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.ViewerLabel;
+
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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/dwtx/jface/viewers/ViewerRow.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,273 @@
+/*******************************************************************************
+ * 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
+ *                                                fix for bug 166346, bug 167325s
+ *                                              - Fix for bug 174355
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.viewers.ViewerRow;
+
+import dwtx.jface.viewers.ViewerCell;
+import dwtx.jface.viewers.ViewerRow;
+import dwtx.jface.viewers.TreePath;
+
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Control;
+import dwt.widgets.Widget;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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;
+
+    /**
+     * 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 abstract Object clone();
+
+    /**
+     * @return the model element
+     */
+    public abstract Object getElement();
+
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result
+                + ((getItem() is null) ? 0 : getItem().toHash());
+        return result;
+    }
+
+    public bool equals(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;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/ViewerSorter.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.ViewerSorter;
+
+import dwtx.jface.viewers.ViewerComparator;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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/dwtx/jface/viewers/WrappedViewerLabelProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,237 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.WrappedViewerLabelProvider;
+
+import dwtx.jface.viewers.ColumnLabelProvider;
+import dwtx.jface.viewers.ILabelProvider;
+import dwtx.jface.viewers.IColorProvider;
+import dwtx.jface.viewers.IFontProvider;
+import dwtx.jface.viewers.IViewerLabelProvider;
+import dwtx.jface.viewers.ITreePathLabelProvider;
+import dwtx.jface.viewers.IBaseLabelProvider;
+import dwtx.jface.viewers.ViewerCell;
+import dwtx.jface.viewers.ViewerLabel;
+import dwtx.jface.viewers.LabelProvider;
+import dwtx.jface.viewers.TreePath;
+
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.Image;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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) {
+        super();
+        labelProvider = defaultLabelProvider;
+        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 dwtx.jface.viewers.IFontProvider#getFont(java.lang.Object)
+     */
+    public Font getFont(Object element) {
+        if (fontProvider is null) {
+            return null;
+        }
+
+        return fontProvider.getFont(element);
+
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.IColorProvider#getBackground(java.lang.Object)
+     */
+    public Color getBackground(Object element) {
+        if (colorProvider is null) {
+            return null;
+        }
+
+        return colorProvider.getBackground(element);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.ILabelProvider#getText(java.lang.Object)
+     */
+    public String getText(Object element) {
+        return getLabelProvider().getText(element);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.ILabelProvider#getImage(java.lang.Object)
+     */
+    public Image getImage(Object element) {
+        return getLabelProvider().getImage(element);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.viewers.IColorProvider#getForeground(java.lang.Object)
+     */
+    public 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 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/dwtx/jface/viewers/deferred/AbstractConcurrentModel.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.deferred.AbstractConcurrentModel;
+
+import dwtx.jface.viewers.deferred.IConcurrentModel;
+import dwtx.jface.viewers.deferred.IConcurrentModelListener;
+
+import dwtx.core.runtime.ListenerList;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 dwtx.jface.viewers.deferred.IConcurrentContentProvider#addListener(dwtx.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 dwtx.jface.viewers.deferred.IConcurrentContentProvider#removeListener(dwtx.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/dwtx/jface/viewers/deferred/AbstractVirtualTable.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,90 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.deferred.AbstractVirtualTable;
+
+import dwt.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 DWT 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 DWT control that this API is wrappering.
+     * @return Control.
+     */
+    public abstract Control getControl();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/deferred/BackgroundContentProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,593 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.deferred.BackgroundContentProvider;
+
+import dwtx.jface.viewers.deferred.ConcurrentTableUpdator;
+import dwtx.jface.viewers.deferred.IConcurrentModel;
+import dwtx.jface.viewers.deferred.IConcurrentModelListener;
+import dwtx.jface.viewers.deferred.ChangeQueue;
+import dwtx.jface.viewers.deferred.FastProgressReporter;
+import dwtx.jface.viewers.deferred.AbstractVirtualTable;
+import dwtx.jface.viewers.deferred.LazySortedCollection;
+
+import dwtx.core.runtime.Assert;
+import dwtx.core.runtime.IProgressMonitor;
+import dwtx.core.runtime.NullProgressMonitor;
+import dwtx.jface.resource.JFaceResources;
+
+import dwtx.jface.viewers.AcceptAllFilter;
+import dwtx.jface.viewers.IFilter;
+
+import dwt.dwthelper.utils;
+import tango.core.Thread;
+
+/**
+ * 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 dwtx.jface.viewers.deferred.IConcurrentModelListener#add(java.lang.Object[])
+            */
+            public void add(Object[] added) {
+                this.outer.add(added);
+            }
+
+            /* (non-Javadoc)
+            * @see dwtx.jface.viewers.deferred.IConcurrentModelListener#remove(java.lang.Object[])
+            */
+            public void remove(Object[] removed) {
+                this.outer.remove(removed);
+            }
+
+            /* (non-Javadoc)
+            * @see dwtx.jface.viewers.deferred.IConcurrentModelListener#setContents(java.lang.Object[])
+            */
+            public void setContents(Object[] newContents) {
+                this.outer.setContents(newContents);
+            }
+
+            /* (non-Javadoc)
+            * @see dwtx.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 Thread 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;
+                    }
+                }
+
+                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 DWT.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 : Thread {
+        private this(String name) {
+            super(/+name+/);
+        }
+
+        public 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.isDaemon = true;
+                sortThread.priority = sortThread.priority - 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/dwtx/jface/viewers/deferred/ChangeQueue.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.deferred.ChangeQueue;
+
+import tango.util.collection.LinkSeq;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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 LinkSeq!(Change) queue;
+    private int workload = 0;
+
+    public this(){
+        queue = new LinkSeq!(Change);
+    }
+
+    /**
+     * 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;
+            LinkSeq!(Change) newQueue = new LinkSeq!(Change);
+            foreach( next; queue ){
+
+                if (next.getType() is ADD || next.getType() is REMOVE || next.getType() is SET) {
+                    continue;
+                }
+
+                newQueue.append(next);
+                workload += next.elements.length;
+            }
+            queue = newQueue;
+        }
+
+        queue.append(toQueue);
+        workload += toQueue.elements.length;
+    }
+
+    /**
+     * Remove the first change from the queue.
+     * @return the first change
+     */
+    public synchronized Change dequeue() {
+        Change result = queue.head;
+        queue.removeHead();
+
+        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.drained();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/deferred/ConcurrentTableUpdator.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,397 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.deferred.ConcurrentTableUpdator;
+
+import dwtx.jface.viewers.deferred.IntHashMap;
+import dwtx.jface.viewers.deferred.AbstractVirtualTable;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+
+
+
+/**
+ * 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/dwtx/jface/viewers/deferred/DeferredContentProvider.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,233 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.deferred.DeferredContentProvider;
+
+import dwtx.jface.viewers.ILazyContentProvider;
+import dwtx.jface.viewers.deferred.AbstractVirtualTable;
+import dwtx.jface.viewers.deferred.BackgroundContentProvider;
+import dwtx.jface.viewers.deferred.IConcurrentModel;
+
+import dwt.widgets.Control;
+import dwt.widgets.Table;
+import dwtx.core.runtime.Assert;
+import dwtx.jface.viewers.AcceptAllFilter;
+import dwtx.jface.viewers.IFilter;
+import dwtx.jface.viewers.ILazyContentProvider;
+import dwtx.jface.viewers.TableViewer;
+import dwtx.jface.viewers.Viewer;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Content provider that performs sorting and filtering in a background thread.
+ * Requires a <code>TableViewer</code> created with the <code>DWT.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 dwtx.jface.viewers.deferred.AbstractVirtualTable#flushCache(java.lang.Object)
+         */
+        public void clear(int index) {
+            viewer.clear(index);
+        }
+
+        /* (non-Javadoc)
+         * @see dwtx.jface.viewers.deferred.AbstractVirtualTable#replace(java.lang.Object, int)
+         */
+        public void replace(Object element, int itemIndex) {
+            viewer.replace(element, itemIndex);
+        }
+
+        /* (non-Javadoc)
+         * @see dwtx.jface.viewers.deferred.AbstractVirtualTable#setItemCount(int)
+         */
+        public void setItemCount(int total) {
+            viewer.setItemCount(total);
+        }
+
+        /* (non-Javadoc)
+         * @see dwtx.jface.viewers.deferred.AbstractVirtualTable#getItemCount()
+         */
+        public int getItemCount() {
+            return viewer.getTable().getItemCount();
+        }
+
+        /* (non-Javadoc)
+         * @see dwtx.jface.viewers.deferred.AbstractVirtualTable#getTopIndex()
+         */
+        public int getTopIndex() {
+            return Math.max(viewer.getTable().getTopIndex() - 1, 0);
+        }
+
+        /* (non-Javadoc)
+         * @see dwtx.jface.viewers.deferred.AbstractVirtualTable#getVisibleItemCount()
+         */
+        public int getVisibleItemCount() {
+            int start = getTopIndex();
+            int itemCount = getItemCount();
+            Table table = viewer.getTable();
+            return Math.min(table.getBounds().height / table.getItemHeight() + 2,
+                    itemCount - start);
+        }
+
+        /* (non-Javadoc)
+         * @see dwtx.jface.viewers.deferred.AbstractVirtualTable#getControl()
+         */
+        public 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 dwtx.jface.viewers.IContentProvider#dispose()
+     */
+    public void dispose() {
+        setProvider(null);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.IContentProvider#inputChanged(dwtx.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 dwtx.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/dwtx/jface/viewers/deferred/FastProgressReporter.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.deferred.FastProgressReporter;
+
+import dwtx.core.runtime.IProgressMonitor;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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/dwtx/jface/viewers/deferred/IConcurrentModel.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.deferred.IConcurrentModel;
+
+import dwtx.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
+ */
+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/dwtx/jface/viewers/deferred/IConcurrentModelListener.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2004, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.viewers.deferred.IConcurrentModelListener;
+
+/**
+ * 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/dwtx/jface/viewers/deferred/IntHashMap.d	Mon Mar 31 00:47:19 2008 +0200
@@ -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 dwtx.jface.viewers.deferred.IntHashMap;
+
+import tango.util.collection.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 HashMap!(Object,int) map;
+
+    /**
+     * @param size
+     * @param loadFactor
+     */
+    public this(int size, float loadFactor) {
+        map = new HashMap!(Object,int);
+//         (size, loadFactor);
+    }
+
+    /**
+     *
+     */
+    public this() {
+        map = new HashMap!(Object,int);
+    }
+
+    /**
+     * @param key
+     */
+    public void remove(Object key) {
+        map.removeKey(key);
+    }
+
+    /**
+     * @param key
+     * @param value
+     */
+    public void put(Object key, int value) {
+        map.add(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( map.containsKey( key )){
+            return map.get(key);
+        }
+        return defaultValue;
+    }
+
+    /**
+     * @param key
+     * @return <code>true</code> if this map contains the given key, <code>false</code> otherwise
+     */
+    public bool containsKey(Object key) {
+        return map.containsKey(key);
+    }
+
+    /**
+     * @return the number of key/value pairs
+     */
+    public int size() {
+        return map.size();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/viewers/deferred/LazySortedCollection.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,1449 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.deferred.LazySortedCollection;
+
+import dwtx.jface.viewers.deferred.IntHashMap;
+import dwtx.jface.viewers.deferred.FastProgressReporter;
+// import java.util.Collection;
+// import java.util.Comparator;
+// import java.util.Iterator;
+import tango.util.collection.model.View;
+
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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( View!(Object) toAdd) {
+        Assert.isNotNull(cast(Object)toAdd);
+        foreach( o; toAdd ){
+            add( o );
+        }
+
+        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/dwtx/jface/viewers/deferred/SetModel.d	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * 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 dwtx.jface.viewers.deferred.SetModel;
+
+import dwtx.jface.viewers.deferred.AbstractConcurrentModel;
+import dwtx.jface.viewers.deferred.IConcurrentModelListener;
+
+import tango.util.collection.HashSet;
+import tango.util.collection.model.View;
+
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * 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!(Object) data;
+
+    public this(){
+        data = new HashSet!(Object);
+    }
+
+    /**
+     * 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();
+        foreach( listener; listeners ){
+            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(View!(Object) 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 dwtx.jface.viewers.deferred.IConcurrentModel#requestUpdate(dwtx.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/dwtx/jface/viewers/deferred/package.html	Mon Mar 31 00:47:19 2008 +0200
@@ -0,0 +1,17 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.5 [en] (Win98; I) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides a framework for viewers that handle deferred contents. 
+<h2>
+Package Specification</h2>
+<p>The deferred viewers are viewers that can handle concurrent updates from a 
+  variety of Threads.<br>
+  &nbsp; 
+</body>
+</html>