diff dwtx/jface/viewers/StructuredViewer.d @ 10:b6c35faf97c8

Viewers
author Frank Benoit <benoit@tionex.de>
date Mon, 31 Mar 2008 00:47:19 +0200
parents
children 644f1334b451
line wrap: on
line diff
--- /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();
+    }
+
+}