changeset 34:b3c8e32d406f

preference
author Frank Benoit <benoit@tionex.de>
date Sat, 05 Apr 2008 01:45:47 +0200
parents f25582573129
children ef4534de0cf9
files dwtx/jface/fieldassist/ControlDecoration.d dwtx/jface/preference/BooleanFieldEditor.d dwtx/jface/preference/BooleanPropertyAction.d dwtx/jface/preference/ColorFieldEditor.d dwtx/jface/preference/ColorSelector.d dwtx/jface/preference/ComboFieldEditor.d dwtx/jface/preference/DirectoryFieldEditor.d dwtx/jface/preference/FieldEditor.d dwtx/jface/preference/FieldEditorPreferencePage.d dwtx/jface/preference/FileFieldEditor.d dwtx/jface/preference/FontFieldEditor.d dwtx/jface/preference/IPersistentPreferenceStore.d dwtx/jface/preference/IPreferenceNode.d dwtx/jface/preference/IPreferencePage.d dwtx/jface/preference/IPreferencePageContainer.d dwtx/jface/preference/IntegerFieldEditor.d dwtx/jface/preference/ListEditor.d dwtx/jface/preference/PathEditor.d dwtx/jface/preference/PreferenceContentProvider.d dwtx/jface/preference/PreferenceConverter.d dwtx/jface/preference/PreferenceDialog.d dwtx/jface/preference/PreferenceLabelProvider.d dwtx/jface/preference/PreferenceManager.d dwtx/jface/preference/PreferenceNode.d dwtx/jface/preference/PreferencePage.d dwtx/jface/preference/PreferenceStore.d dwtx/jface/preference/RadioGroupFieldEditor.d dwtx/jface/preference/ScaleFieldEditor.d dwtx/jface/preference/StringButtonFieldEditor.d dwtx/jface/preference/StringFieldEditor.d
diffstat 30 files changed, 8304 insertions(+), 116 deletions(-) [+]
line wrap: on
line diff
--- a/dwtx/jface/fieldassist/ControlDecoration.d	Thu Apr 03 20:41:52 2008 +0200
+++ b/dwtx/jface/fieldassist/ControlDecoration.d	Sat Apr 05 01:45:47 2008 +0200
@@ -86,7 +86,9 @@
     /**
      * Cached platform flags for dealing with platform-specific issues.
      */
-    private static bool CARBON = "carbon".equals(DWT.getPlatform()); //$NON-NLS-1$
+    private static bool CARBON(){
+        return "carbon".equals(DWT.getPlatform()); //$NON-NLS-1$
+    }
 
     /**
      * The associated control
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/BooleanFieldEditor.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,261 @@
+/*******************************************************************************
+ * 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.preference.BooleanFieldEditor;
+
+import dwtx.jface.preference.FieldEditor;
+
+import dwt.DWT;
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.events.SelectionAdapter;
+import dwt.events.SelectionEvent;
+import dwt.layout.GridData;
+import dwt.widgets.Button;
+import dwt.widgets.Composite;
+import dwt.widgets.Label;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A field editor for a bool type preference.
+ */
+public class BooleanFieldEditor : FieldEditor {
+
+    /**
+     * Style constant (value <code>0</code>) indicating the default
+     * layout where the field editor's check box appears to the left
+     * of the label.
+     */
+    public static const int DEFAULT = 0;
+
+    /**
+     * Style constant (value <code>1</code>) indicating a layout
+     * where the field editor's label appears on the left
+     * with a check box on the right.
+     */
+    public static const int SEPARATE_LABEL = 1;
+
+    /**
+     * Style bits. Either <code>DEFAULT</code> or
+     * <code>SEPARATE_LABEL</code>.
+     */
+    private int style;
+
+    /**
+     * The previously selected, or "before", value.
+     */
+    private bool wasSelected;
+
+    /**
+     * The checkbox control, or <code>null</code> if none.
+     */
+    private Button checkBox = null;
+
+    /**
+     * Creates a new bool field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a bool field editor in the given style.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param style the style, either <code>DEFAULT</code> or
+     *   <code>SEPARATE_LABEL</code>
+     * @param parent the parent of the field editor's control
+     * @see #DEFAULT
+     * @see #SEPARATE_LABEL
+     */
+    public this(String name, String labelText, int style,
+            Composite parent) {
+        init(name, labelText);
+        this.style = style;
+        createControl(parent);
+    }
+
+    /**
+     * Creates a bool field editor in the default style.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param label the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String label, Composite parent) {
+        this(name, label, DEFAULT, parent);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void adjustForNumColumns(int numColumns) {
+        if (style is SEPARATE_LABEL) {
+            numColumns--;
+        }
+        (cast(GridData) checkBox.getLayoutData()).horizontalSpan = numColumns;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doFillIntoGrid(Composite parent, int numColumns) {
+        String text = getLabelText();
+        switch (style) {
+        case SEPARATE_LABEL:
+            getLabelControl(parent);
+            numColumns--;
+            text = null;
+        default:
+            checkBox = getChangeControl(parent);
+            GridData gd = new GridData();
+            gd.horizontalSpan = numColumns;
+            checkBox.setLayoutData(gd);
+            if (text !is null) {
+                checkBox.setText(text);
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     * Loads the value from the preference store and sets it to
+     * the check box.
+     */
+    protected void doLoad() {
+        if (checkBox !is null) {
+            bool value = getPreferenceStore()
+                    .getBoolean(getPreferenceName());
+            checkBox.setSelection(value);
+            wasSelected = value;
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     * Loads the default value from the preference store and sets it to
+     * the check box.
+     */
+    protected void doLoadDefault() {
+        if (checkBox !is null) {
+            bool value = getPreferenceStore().getDefaultBoolean(
+                    getPreferenceName());
+            checkBox.setSelection(value);
+            wasSelected = value;
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doStore() {
+        getPreferenceStore().setValue(getPreferenceName(),
+                checkBox.getSelection());
+    }
+
+    /**
+     * Returns this field editor's current value.
+     *
+     * @return the value
+     */
+    public bool getBooleanValue() {
+        return checkBox.getSelection();
+    }
+
+    /**
+     * Returns the change button for this field editor.
+     * @param parent The Composite to create the receiver in.
+     *
+     * @return the change button
+     */
+    protected Button getChangeControl(Composite parent) {
+        if (checkBox is null) {
+            checkBox = new Button(parent, DWT.CHECK | DWT.LEFT);
+            checkBox.setFont(parent.getFont());
+            checkBox.addSelectionListener(new class SelectionAdapter {
+                public void widgetSelected(SelectionEvent e) {
+                    bool isSelected = checkBox.getSelection();
+                    valueChanged(wasSelected, isSelected);
+                    wasSelected = isSelected;
+                }
+            });
+            checkBox.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    checkBox = null;
+                }
+            });
+        } else {
+            checkParent(checkBox, parent);
+        }
+        return checkBox;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public int getNumberOfControls() {
+        switch (style) {
+        case SEPARATE_LABEL:
+            return 2;
+        default:
+            return 1;
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public void setFocus() {
+        if (checkBox !is null) {
+            checkBox.setFocus();
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public void setLabelText(String text) {
+        super.setLabelText(text);
+        Label label = getLabelControl();
+        if (label is null && checkBox !is null) {
+            checkBox.setText(text);
+        }
+    }
+
+    /**
+     * Informs this field editor's listener, if it has one, about a change
+     * to the value (<code>VALUE</code> property) provided that the old and
+     * new values are different.
+     *
+     * @param oldValue the old value
+     * @param newValue the new value
+     */
+    protected void valueChanged(bool oldValue, bool newValue) {
+        setPresentsDefaultValue(false);
+        if (oldValue !is newValue) {
+            fireStateChanged(VALUE, oldValue, newValue);
+        }
+    }
+
+    /*
+     * @see FieldEditor.setEnabled
+     */
+    public void setEnabled(bool enabled, Composite parent) {
+        //Only call super if there is a label already
+        if (style is SEPARATE_LABEL) {
+            super.setEnabled(enabled, parent);
+        }
+        getChangeControl(parent).setEnabled(enabled);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/BooleanPropertyAction.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.preference.BooleanPropertyAction;
+
+import dwtx.jface.preference.IPreferenceStore;
+
+import dwtx.jface.action.Action;
+import dwtx.jface.util.IPropertyChangeListener;
+import dwtx.jface.util.PropertyChangeEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * The BooleanPropertyAction is an action that set the values of a
+ * bool property in the preference store.
+ */
+
+public class BooleanPropertyAction : Action {
+
+    private IPreferenceStore preferenceStore;
+
+    private String property;
+
+    /**
+     * Create a new instance of the receiver.
+     * @param title The displayable name of the action.
+     * @param preferenceStore The preference store to propogate changes to
+     * @param property The property that is being updated
+     * @throws IllegalArgumentException Thrown if preferenceStore or
+     * property are <code>null</code>.
+     */
+    public this(String title,
+            IPreferenceStore preferenceStore, String property) {
+        super(title, AS_CHECK_BOX);
+
+        if (preferenceStore is null || property is null) {
+            throw new IllegalArgumentException(null);
+        }
+
+        this.preferenceStore = preferenceStore;
+        this.property = property;
+        final String finalProprety = property;
+
+        preferenceStore
+                .addPropertyChangeListener(new class IPropertyChangeListener {
+                    public void propertyChange(PropertyChangeEvent event) {
+                        if (finalProprety.equals(event.getProperty())) {
+                            setChecked(cast(bool)(Boolean.TRUE == event.getNewValue() ));
+                        }
+                    }
+                });
+
+        setChecked(preferenceStore.getBoolean(property));
+    }
+
+    /*
+     *  (non-Javadoc)
+     * @see dwtx.jface.action.IAction#run()
+     */
+    public void run() {
+        preferenceStore.setValue(property, isChecked());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/ColorFieldEditor.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,193 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.preference.ColorFieldEditor;
+
+import dwtx.jface.preference.FieldEditor;
+import dwtx.jface.preference.ColorSelector;
+import dwtx.jface.preference.PreferenceConverter;
+
+import dwt.graphics.Font;
+import dwt.graphics.GC;
+import dwt.graphics.Point;
+import dwt.layout.GridData;
+import dwt.widgets.Button;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwtx.jface.resource.JFaceResources;
+import dwtx.jface.util.IPropertyChangeListener;
+import dwtx.jface.util.PropertyChangeEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A field editor for a color type preference.
+ */
+public class ColorFieldEditor : FieldEditor {
+
+    /**
+     * The color selector, or <code>null</code> if none.
+     */
+    private ColorSelector colorSelector;
+
+    /**
+     * Creates a new color field editor
+     */
+    protected this() {
+        //No default behavior
+    }
+
+    /**
+     * Creates a color field editor.
+     *
+     * @param name
+     *            the name of the preference this field editor works on
+     * @param labelText
+     *            the label text of the field editor
+     * @param parent
+     *            the parent of the field editor's control
+     */
+    public this(String name, String labelText, Composite parent) {
+        super(name, labelText, parent);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on FieldEditor.
+     */
+    protected void adjustForNumColumns(int numColumns) {
+        (cast(GridData) colorSelector.getButton().getLayoutData()).horizontalSpan = numColumns - 1;
+    }
+
+    /**
+     * Computes the size of the color image displayed on the button.
+     * <p>
+     * This is an internal method and should not be called by clients.
+     * </p>
+     *
+     * @param window
+     *            the window to create a GC on for calculation.
+     * @return Point The image size
+     *
+     */
+    protected Point computeImageSize(Control window) {
+        // Make the image height as high as a corresponding character. This
+        // makes sure that the button has the same size as a "normal" text
+        // button.
+        GC gc = new GC(window);
+        Font f = JFaceResources.getFontRegistry().get(
+                JFaceResources.DEFAULT_FONT);
+        gc.setFont(f);
+        int height = gc.getFontMetrics().getHeight();
+        gc.dispose();
+        Point p = new Point(height * 3 - 6, height);
+        return p;
+    }
+
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.preference.FieldEditor#doFillIntoGrid(dwt.widgets.Composite, int)
+     */
+    protected void doFillIntoGrid(Composite parent, int numColumns) {
+        Control control = getLabelControl(parent);
+        GridData gd = new GridData();
+        gd.horizontalSpan = numColumns - 1;
+        control.setLayoutData(gd);
+
+        Button colorButton = getChangeControl(parent);
+        colorButton.setLayoutData(new GridData());
+
+    }
+
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.preference.FieldEditor#doLoad()
+     */
+    protected void doLoad() {
+        if (colorSelector is null) {
+            return;
+        }
+        colorSelector.setColorValue(PreferenceConverter.getColor(
+                getPreferenceStore(), getPreferenceName()));
+    }
+
+    /*
+     * (non-Javadoc) Method declared on FieldEditor.
+     */
+    protected void doLoadDefault() {
+        if (colorSelector is null) {
+            return;
+        }
+        colorSelector.setColorValue(PreferenceConverter.getDefaultColor(
+                getPreferenceStore(), getPreferenceName()));
+    }
+
+    /*
+     * (non-Javadoc) Method declared on FieldEditor.
+     */
+    protected void doStore() {
+        PreferenceConverter.setValue(getPreferenceStore(), getPreferenceName(),
+                colorSelector.getColorValue());
+    }
+
+    /**
+     * Get the color selector used by the receiver.
+     *
+     * @return ColorSelector/
+     */
+    public ColorSelector getColorSelector() {
+        return colorSelector;
+    }
+
+    /**
+     * Returns the change button for this field editor.
+     *
+     * @param parent
+     *            The control to create the button in if required.
+     * @return the change button
+     */
+    protected Button getChangeControl(Composite parent) {
+        if (colorSelector is null) {
+            colorSelector = new ColorSelector(parent);
+            colorSelector.addListener(new class IPropertyChangeListener {
+                // forward the property change of the color selector
+                public void propertyChange(PropertyChangeEvent event) {
+                    this.outer.fireValueChanged(event.getProperty(),
+                            event.getOldValue(), event.getNewValue());
+                    setPresentsDefaultValue(false);
+                }
+            });
+
+        } else {
+            checkParent(colorSelector.getButton(), parent);
+        }
+        return colorSelector.getButton();
+    }
+
+    /*
+     * (non-Javadoc) Method declared on FieldEditor.
+     */
+    public int getNumberOfControls() {
+        return 2;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.preference.FieldEditor#setEnabled(bool,
+     *      dwt.widgets.Composite)
+     */
+    public void setEnabled(bool enabled, Composite parent) {
+        super.setEnabled(enabled, parent);
+        getChangeControl(parent).setEnabled(enabled);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/ColorSelector.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.preference.ColorSelector;
+
+
+import dwt.DWT;
+import dwt.accessibility.AccessibleAdapter;
+import dwt.accessibility.AccessibleEvent;
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.events.SelectionAdapter;
+import dwt.events.SelectionEvent;
+import dwt.graphics.Color;
+import dwt.graphics.Font;
+import dwt.graphics.GC;
+import dwt.graphics.Image;
+import dwt.graphics.Point;
+import dwt.graphics.RGB;
+import dwt.widgets.Button;
+import dwt.widgets.ColorDialog;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Display;
+import dwtx.core.commands.common.EventManager;
+import dwtx.jface.resource.JFaceResources;
+import dwtx.jface.util.IPropertyChangeListener;
+import dwtx.jface.util.PropertyChangeEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * The <code>ColorSelector</code> is a wrapper for a button that displays a
+ * selected <code>Color</code> and allows the user to change the selection.
+ */
+public class ColorSelector : EventManager {
+    /**
+     * Property name that signifies the selected color of this
+     * <code>ColorSelector</code> has changed.
+     *
+     * @since 3.0
+     */
+    public static const String PROP_COLORCHANGE = "colorValue"; //$NON-NLS-1$
+
+    private Button fButton;
+
+    private Color fColor;
+
+    private RGB fColorValue;
+
+    private Point fExtent;
+
+    private Image fImage;
+
+    /**
+     * Create a new instance of the reciever and the button that it wrappers in
+     * the supplied parent <code>Composite</code>.
+     *
+     * @param parent
+     *            The parent of the button.
+     */
+    public this(Composite parent) {
+        fButton = new Button(parent, DWT.PUSH);
+        fExtent = computeImageSize(parent);
+        fImage = new Image(parent.getDisplay(), fExtent.x, fExtent.y);
+        GC gc = new GC(fImage);
+        gc.setBackground(fButton.getBackground());
+        gc.fillRectangle(0, 0, fExtent.x, fExtent.y);
+        gc.dispose();
+        fButton.setImage(fImage);
+        fButton.addSelectionListener(new class SelectionAdapter {
+            public void widgetSelected(SelectionEvent event) {
+                open();
+            }
+        });
+        fButton.addDisposeListener(new class DisposeListener {
+            public void widgetDisposed(DisposeEvent event) {
+                if (fImage !is null) {
+                    fImage.dispose();
+                    fImage = null;
+                }
+                if (fColor !is null) {
+                    fColor.dispose();
+                    fColor = null;
+                }
+            }
+        });
+        fButton.getAccessible().addAccessibleListener(new class AccessibleAdapter {
+            /*
+             * (non-Javadoc)
+             *
+             * @see dwt.accessibility.AccessibleAdapter#getName(dwt.accessibility.AccessibleEvent)
+             */
+            public void getName(AccessibleEvent e) {
+                e.result = JFaceResources.getString("ColorSelector.Name"); //$NON-NLS-1$
+            }
+        });
+    }
+
+    /**
+     * Adds a property change listener to this <code>ColorSelector</code>.
+     * Events are fired when the color in the control changes via the user
+     * clicking an selecting a new one in the color dialog. No event is fired in
+     * the case where <code>setColorValue(RGB)</code> is invoked.
+     *
+     * @param listener
+     *            a property change listener
+     * @since 3.0
+     */
+    public void addListener(IPropertyChangeListener listener) {
+        addListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Compute the size of the image to be displayed.
+     *
+     * @param window -
+     *            the window used to calculate
+     * @return <code>Point</code>
+     */
+    private Point computeImageSize(Control window) {
+        GC gc = new GC(window);
+        Font f = JFaceResources.getFontRegistry().get(
+                JFaceResources.DIALOG_FONT);
+        gc.setFont(f);
+        int height = gc.getFontMetrics().getHeight();
+        gc.dispose();
+        Point p = new Point(height * 3 - 6, height);
+        return p;
+    }
+
+    /**
+     * Get the button control being wrappered by the selector.
+     *
+     * @return <code>Button</code>
+     */
+    public Button getButton() {
+        return fButton;
+    }
+
+    /**
+     * Return the currently displayed color.
+     *
+     * @return <code>RGB</code>
+     */
+    public RGB getColorValue() {
+        return fColorValue;
+    }
+
+    /**
+     * Removes the given listener from this <code>ColorSelector</code>. Has
+     * no affect if the listener is not registered.
+     *
+     * @param listener
+     *            a property change listener
+     * @since 3.0
+     */
+    public void removeListener(IPropertyChangeListener listener) {
+        removeListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Set the current color value and update the control.
+     *
+     * @param rgb
+     *            The new color.
+     */
+    public void setColorValue(RGB rgb) {
+        fColorValue = rgb;
+        updateColorImage();
+    }
+
+    /**
+     * Set whether or not the button is enabled.
+     *
+     * @param state
+     *            the enabled state.
+     */
+    public void setEnabled(bool state) {
+        getButton().setEnabled(state);
+    }
+
+    /**
+     * Update the image being displayed on the button using the current color
+     * setting.
+     */
+    protected void updateColorImage() {
+        Display display = fButton.getDisplay();
+        GC gc = new GC(fImage);
+        gc.setForeground(display.getSystemColor(DWT.COLOR_BLACK));
+        gc.drawRectangle(0, 2, fExtent.x - 1, fExtent.y - 4);
+        if (fColor !is null) {
+            fColor.dispose();
+        }
+        fColor = new Color(display, fColorValue);
+        gc.setBackground(fColor);
+        gc.fillRectangle(1, 3, fExtent.x - 2, fExtent.y - 5);
+        gc.dispose();
+        fButton.setImage(fImage);
+    }
+
+    /**
+     * Activate the editor for this selector. This causes the color selection
+     * dialog to appear and wait for user input.
+     *
+     * @since 3.2
+     */
+    public void open() {
+        ColorDialog colorDialog = new ColorDialog(fButton.getShell());
+        colorDialog.setRGB(fColorValue);
+        RGB newColor = colorDialog.open();
+        if (newColor !is null) {
+            RGB oldValue = fColorValue;
+            fColorValue = newColor;
+            final Object[] finalListeners = getListeners();
+            if (finalListeners.length > 0) {
+                PropertyChangeEvent pEvent = new PropertyChangeEvent(
+                        this, PROP_COLORCHANGE, oldValue, newColor);
+                for (int i = 0; i < finalListeners.length; ++i) {
+                    IPropertyChangeListener listener = cast(IPropertyChangeListener) finalListeners[i];
+                    listener.propertyChange(pEvent);
+                }
+            }
+            updateColorImage();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/ComboFieldEditor.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,213 @@
+/*******************************************************************************
+ * 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.preference.ComboFieldEditor;
+
+import dwtx.jface.preference.FieldEditor;
+
+import dwt.DWT;
+import dwt.events.SelectionAdapter;
+import dwt.events.SelectionEvent;
+import dwt.layout.GridData;
+import dwt.widgets.Combo;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A field editor for a combo box that allows the drop-down selection of one of
+ * a list of items.
+ *
+ * @since 3.3
+ */
+public class ComboFieldEditor : FieldEditor {
+
+    /**
+     * The <code>Combo</code> widget.
+     */
+    private Combo fCombo;
+
+    /**
+     * The value (not the name) of the currently selected item in the Combo widget.
+     */
+    private String fValue;
+
+    /**
+     * The names (labels) and underlying values to populate the combo widget.  These should be
+     * arranged as: { {name1, value1}, {name2, value2}, ...}
+     */
+    private String[][] fEntryNamesAndValues;
+
+    /**
+     * Create the combo box field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param entryNamesAndValues the names (labels) and underlying values to populate the combo widget.  These should be
+     * arranged as: { {name1, value1}, {name2, value2}, ...}
+     * @param parent the parent composite
+     */
+    public this(String name, String labelText, String[][] entryNamesAndValues, Composite parent) {
+        init(name, labelText);
+        Assert.isTrue(checkArray(entryNamesAndValues));
+        fEntryNamesAndValues = entryNamesAndValues;
+        createControl(parent);
+    }
+
+    /**
+     * Checks whether given <code>String[][]</code> is of "type"
+     * <code>String[][2]</code>.
+     *
+     * @return <code>true</code> if it is ok, and <code>false</code> otherwise
+     */
+    private bool checkArray(String[][] table) {
+        if (table is null) {
+            return false;
+        }
+        for (int i = 0; i < table.length; i++) {
+            String[] array = table[i];
+            if (array is null || array.length !is 2) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.preference.FieldEditor#adjustForNumColumns(int)
+     */
+    protected void adjustForNumColumns(int numColumns) {
+        if (numColumns > 1) {
+            Control control = getLabelControl();
+            int left = numColumns;
+            if (control !is null) {
+                (cast(GridData)control.getLayoutData()).horizontalSpan = 1;
+                left = left - 1;
+            }
+            (cast(GridData)fCombo.getLayoutData()).horizontalSpan = left;
+        } else {
+            Control control = getLabelControl();
+            if (control !is null) {
+                (cast(GridData)control.getLayoutData()).horizontalSpan = 1;
+            }
+            (cast(GridData)fCombo.getLayoutData()).horizontalSpan = 1;
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.preference.FieldEditor#doFillIntoGrid(dwt.widgets.Composite, int)
+     */
+    protected void doFillIntoGrid(Composite parent, int numColumns) {
+        int comboC = 1;
+        if (numColumns > 1) {
+            comboC = numColumns - 1;
+        }
+        Control control = getLabelControl(parent);
+        GridData gd = new GridData();
+        gd.horizontalSpan = 1;
+        control.setLayoutData(gd);
+        control = getComboBoxControl(parent);
+        gd = new GridData();
+        gd.horizontalSpan = comboC;
+        gd.horizontalAlignment = GridData.FILL;
+        control.setLayoutData(gd);
+        control.setFont(parent.getFont());
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.preference.FieldEditor#doLoad()
+     */
+    protected void doLoad() {
+        updateComboForValue(getPreferenceStore().getString(getPreferenceName()));
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.preference.FieldEditor#doLoadDefault()
+     */
+    protected void doLoadDefault() {
+        updateComboForValue(getPreferenceStore().getDefaultString(getPreferenceName()));
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.preference.FieldEditor#doStore()
+     */
+    protected void doStore() {
+        if (fValue is null) {
+            getPreferenceStore().setToDefault(getPreferenceName());
+            return;
+        }
+        getPreferenceStore().setValue(getPreferenceName(), fValue);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.preference.FieldEditor#getNumberOfControls()
+     */
+    public int getNumberOfControls() {
+        return 2;
+    }
+
+    /*
+     * Lazily create and return the Combo control.
+     */
+    private Combo getComboBoxControl(Composite parent) {
+        if (fCombo is null) {
+            fCombo = new Combo(parent, DWT.READ_ONLY);
+            fCombo.setFont(parent.getFont());
+            for (int i = 0; i < fEntryNamesAndValues.length; i++) {
+                fCombo.add(fEntryNamesAndValues[i][0], i);
+            }
+
+            fCombo.addSelectionListener(new class SelectionAdapter {
+                public void widgetSelected(SelectionEvent evt) {
+                    String oldValue = fValue;
+                    String name = fCombo.getText();
+                    fValue = getValueForName(name);
+                    setPresentsDefaultValue(false);
+                    fireValueChanged(VALUE, stringcast(oldValue), stringcast(fValue));
+                }
+            });
+        }
+        return fCombo;
+    }
+
+    /*
+     * Given the name (label) of an entry, return the corresponding value.
+     */
+    private String getValueForName(String name) {
+        for (int i = 0; i < fEntryNamesAndValues.length; i++) {
+            String[] entry = fEntryNamesAndValues[i];
+            if (name.equals(entry[0])) {
+                return entry[1];
+            }
+        }
+        return fEntryNamesAndValues[0][0];
+    }
+
+    /*
+     * Set the name in the combo widget to match the specified value.
+     */
+    private void updateComboForValue(String value) {
+        fValue = value;
+        for (int i = 0; i < fEntryNamesAndValues.length; i++) {
+            if (value.equals(fEntryNamesAndValues[i][1])) {
+                fCombo.setText(fEntryNamesAndValues[i][0]);
+                return;
+            }
+        }
+        if (fEntryNamesAndValues.length > 0) {
+            fValue = fEntryNamesAndValues[0][1];
+            fCombo.setText(fEntryNamesAndValues[0][0]);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/DirectoryFieldEditor.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * 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.preference.DirectoryFieldEditor;
+
+import dwtx.jface.preference.StringButtonFieldEditor;
+// import java.io.File;
+
+import dwt.DWT;
+import dwt.widgets.Composite;
+import dwt.widgets.DirectoryDialog;
+import dwtx.jface.resource.JFaceResources;
+
+import dwt.dwthelper.utils;
+import tango.io.FilePath;
+import tango.io.FileSystem;
+
+/**
+ * A field editor for a directory path type preference. A standard directory
+ * dialog appears when the user presses the change button.
+ */
+public class DirectoryFieldEditor : StringButtonFieldEditor {
+    /**
+     * Creates a new directory field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a directory field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText, Composite parent) {
+        init(name, labelText);
+        setErrorMessage(JFaceResources
+                .getString("DirectoryFieldEditor.errorMessage"));//$NON-NLS-1$
+        setChangeButtonText(JFaceResources.getString("openBrowse"));//$NON-NLS-1$
+        setValidateStrategy(VALIDATE_ON_FOCUS_LOST);
+        createControl(parent);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StringButtonFieldEditor.
+     * Opens the directory chooser dialog and returns the selected directory.
+     */
+    protected String changePressed() {
+        auto f = new FilePath(getTextControl().getText());
+        if (!f.exists()) {
+            f = cast(FilePath)null;
+        }
+        auto d = getDirectory(f);
+        if (d is null) {
+            return null;
+        }
+
+        return FileSystem.toAbsolute( d ).toString;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StringFieldEditor.
+     * Checks whether the text input field contains a valid directory.
+     */
+    protected bool doCheckState() {
+        String fileName = getTextControl().getText();
+        fileName = fileName.trim();
+        if (fileName.length is 0 && isEmptyStringAllowed()) {
+            return true;
+        }
+        auto file = new FilePath(fileName);
+        return file.isFolder();
+    }
+
+    /**
+     * Helper that opens the directory chooser dialog.
+     * @param startingDirectory The directory the dialog will open in.
+     * @return File File or <code>null</code>.
+     *
+     */
+    private FilePath getDirectory(FilePath startingDirectory) {
+
+        DirectoryDialog fileDialog = new DirectoryDialog(getShell(), DWT.OPEN);
+        if (startingDirectory !is null) {
+            fileDialog.setFilterPath(startingDirectory.path);
+        }
+        String dir = fileDialog.open();
+        if (dir !is null) {
+            dir = dir.trim();
+            if (dir.length > 0) {
+                return new FilePath(dir);
+            }
+        }
+
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/FieldEditor.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,731 @@
+/*******************************************************************************
+ * 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.preference.FieldEditor;
+
+import dwtx.jface.preference.IPreferenceStore;
+import dwtx.jface.preference.PreferencePage;
+
+import dwt.DWT;
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.graphics.FontMetrics;
+import dwt.graphics.GC;
+import dwt.layout.GridData;
+import dwt.layout.GridLayout;
+import dwt.widgets.Button;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Label;
+import dwtx.core.runtime.Assert;
+import dwtx.jface.dialogs.DialogPage;
+import dwtx.jface.dialogs.IDialogConstants;
+import dwtx.jface.resource.JFaceResources;
+import dwtx.jface.util.IPropertyChangeListener;
+import dwtx.jface.util.PropertyChangeEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Abstract base class for all field editors.
+ * <p>
+ * A field editor presents the value of a preference to the end
+ * user. The value is loaded from a preference store; if
+ * modified by the end user, the value is validated and eventually
+ * stored back to the preference store. A field editor reports
+ * an event when the value, or the validity of the value, changes.
+ * </p>
+ * <p>
+ * Field editors should be used in conjunction with a field
+ * editor preference page (<code>FieldEditorPreferencePage</code>)
+ * which coordinates everything and provides the message line
+ * which display messages emanating from the editor.
+ * </p>
+ * <p>
+ * This package contains ready-to-use field editors for various
+ * types of preferences:
+ * <ul>
+ *   <li><code>BooleanFieldEditor</code> - booleans</li>
+ *   <li><code>IntegerFieldEditor</code> - integers</li>
+ *   <li><code>StringFieldEditor</code> - text strings</li>
+ *   <li><code>RadioGroupFieldEditor</code> - enumerations</li>
+ *   <li><code>ColorFieldEditor</code> - RGB colors</li>
+ *   <li><code>FontFieldEditor</code> - fonts</li>
+ *   <li><code>DirectoryFieldEditor</code> - directories</li>
+ *   <li><code>FileFieldEditor</code> - files</li>
+ *   <li><code>PathEditor</code> - paths</li>
+ * </ul>
+ * </p>
+ */
+public abstract class FieldEditor {
+
+    /**
+     * Property name constant (value <code>"field_editor_is_valid"</code>)
+     * to signal a change in the validity of the value of this field editor.
+     */
+    public static const String IS_VALID = "field_editor_is_valid";//$NON-NLS-1$
+
+    /**
+     * Property name constant (value <code>"field_editor_value"</code>)
+     * to signal a change in the value of this field editor.
+     */
+    public static const String VALUE = "field_editor_value";//$NON-NLS-1$
+
+    /**
+     * Gap between label and control.
+     */
+    protected static const int HORIZONTAL_GAP = 8;
+
+    /**
+     * The preference store, or <code>null</code> if none.
+     */
+    private IPreferenceStore preferenceStore = null;
+
+    /**
+     * The name of the preference displayed in this field editor.
+     */
+    private String preferenceName;
+
+    /**
+     * Indicates whether the default value is currently displayed,
+     * initially <code>false</code>.
+     */
+    private bool isDefaultPresented = false;
+
+    /**
+     * The label's text.
+     */
+    private String labelText;
+
+    /**
+     * The label control.
+     */
+    private Label label;
+
+    /**
+     * Listener, or <code>null</code> if none
+     */
+    private IPropertyChangeListener propertyChangeListener;
+
+    /**
+     * The page containing this field editor
+     */
+    private DialogPage page;
+
+    /**
+     * Creates a new field editor.
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a new field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    protected this(String name, String labelText, Composite parent) {
+        init(name, labelText);
+        createControl(parent);
+    }
+
+    /**
+     * Adjusts the horizontal span of this field editor's basic controls.
+     * <p>
+     * Subclasses must implement this method to adjust the horizontal span
+     * of controls so they appear correct in the given number of columns.
+     * </p>
+     * <p>
+     * The number of columns will always be equal to or greater than the
+     * value returned by this editor's <code>getNumberOfControls</code> method.
+     *
+     * @param numColumns the number of columns
+     */
+    protected abstract void adjustForNumColumns(int numColumns);
+    package void adjustForNumColumns_package(int numColumns){
+        adjustForNumColumns(numColumns);
+    }
+
+    /**
+     * Applies a font.
+     * <p>
+     * The default implementation of this framework method
+     * does nothing. Subclasses should override this method
+     * if they want to change the font of the DWT control to
+     * a value different than the standard dialog font.
+     * </p>
+     */
+    protected void applyFont() {
+    }
+    package void applyFont_package() {
+        applyFont();
+    }
+
+    /**
+     * Checks if the given parent is the current parent of the
+     * supplied control; throws an (unchecked) exception if they
+     * are not correctly related.
+     *
+     * @param control the control
+     * @param parent the parent control
+     */
+    protected void checkParent(Control control, Composite parent) {
+        Assert.isTrue(control.getParent() is parent, "Different parents");//$NON-NLS-1$
+    }
+
+    /**
+     * Clears the error message from the message line.
+     */
+    protected void clearErrorMessage() {
+        if (page !is null) {
+            page.setErrorMessage(null);
+        }
+    }
+
+    /**
+     * Clears the normal message from the message line.
+     */
+    protected void clearMessage() {
+        if (page !is null) {
+            page.setMessage(null);
+        }
+    }
+
+    /**
+     * Returns the number of pixels corresponding to the
+     * given number of horizontal dialog units.
+     * <p>
+     * Clients may call this framework method, but should not override it.
+     * </p>
+     *
+     * @param control the control being sized
+     * @param dlus the number of horizontal dialog units
+     * @return the number of pixels
+     */
+    protected int convertHorizontalDLUsToPixels(Control control, int dlus) {
+        GC gc = new GC(control);
+        gc.setFont(control.getFont());
+        int averageWidth = gc.getFontMetrics().getAverageCharWidth();
+        gc.dispose();
+
+        double horizontalDialogUnitSize = averageWidth * 0.25;
+
+        return cast(int) Math.round(dlus * horizontalDialogUnitSize);
+    }
+
+    /**
+     * Returns the number of pixels corresponding to the
+     * given number of vertical dialog units.
+     * <p>
+     * Clients may call this framework method, but should not override it.
+     * </p>
+     *
+     * @param control the control being sized
+     * @param dlus the number of vertical dialog units
+     * @return the number of pixels
+     */
+    protected int convertVerticalDLUsToPixels(Control control, int dlus) {
+        GC gc = new GC(control);
+        gc.setFont(control.getFont());
+        int height = gc.getFontMetrics().getHeight();
+        gc.dispose();
+
+        double verticalDialogUnitSize = height * 0.125;
+
+        return cast(int) Math.round(dlus * verticalDialogUnitSize);
+    }
+
+    /**
+     * Creates this field editor's main control containing all of its
+     * basic controls.
+     *
+     * @param parent the parent control
+     */
+    protected void createControl(Composite parent) {
+        GridLayout layout = new GridLayout();
+        layout.numColumns = getNumberOfControls();
+        layout.marginWidth = 0;
+        layout.marginHeight = 0;
+        layout.horizontalSpacing = HORIZONTAL_GAP;
+        parent.setLayout(layout);
+        doFillIntoGrid(parent, layout.numColumns);
+    }
+
+    /**
+     * Disposes the DWT resources used by this field editor.
+     */
+    public void dispose() {
+        // nothing to dispose
+    }
+
+    /**
+     * Fills this field editor's basic controls into the given parent.
+     * <p>
+     * Subclasses must implement this method to create the controls
+     * for this field editor.
+     * </p>
+     *
+     * @param parent the composite used as a parent for the basic controls;
+     *  the parent's layout must be a <code>GridLayout</code>
+     * @param numColumns the number of columns
+     */
+    protected abstract void doFillIntoGrid(Composite parent, int numColumns);
+
+    /**
+     * Initializes this field editor with the preference value from
+     * the preference store.
+     * <p>
+     * Subclasses must implement this method to properly initialize
+     * the field editor.
+     * </p>
+     */
+    protected abstract void doLoad();
+
+    /**
+     * Initializes this field editor with the default preference value from
+     * the preference store.
+     * <p>
+     * Subclasses must implement this method to properly initialize
+     * the field editor.
+     * </p>
+     */
+    protected abstract void doLoadDefault();
+
+    /**
+     * Stores the preference value from this field editor into
+     * the preference store.
+     * <p>
+     * Subclasses must implement this method to save the entered value
+     * into the preference store.
+     * </p>
+     */
+    protected abstract void doStore();
+
+    /**
+     * Fills this field editor's basic controls into the given parent.
+     *
+     * @param parent the composite used as a parent for the basic controls;
+     *  the parent's layout must be a <code>GridLayout</code>
+     * @param numColumns the number of columns
+     */
+    public void fillIntoGrid(Composite parent, int numColumns) {
+        Assert.isTrue(numColumns >= getNumberOfControls());
+        Assert.isTrue(null !is cast(GridLayout)parent.getLayout() );
+        doFillIntoGrid(parent, numColumns);
+    }
+
+    /**
+     * Informs this field editor's listener, if it has one, about a change to
+     * one of this field editor's bool-valued properties. Does nothing
+     * if the old and new values are the same.
+     *
+     * @param property the field editor property name,
+     *   such as <code>VALUE</code> or <code>IS_VALID</code>
+     * @param oldValue the old value
+     * @param newValue the new value
+     */
+    protected void fireStateChanged(String property, bool oldValue,
+            bool newValue) {
+        if (oldValue is newValue) {
+            return;
+        }
+        fireValueChanged(property, oldValue ? Boolean.TRUE : Boolean.FALSE, newValue ? Boolean.TRUE : Boolean.FALSE);
+    }
+
+    /**
+     * Informs this field editor's listener, if it has one, about a change to
+     * one of this field editor's properties.
+     *
+     * @param property the field editor property name,
+     *   such as <code>VALUE</code> or <code>IS_VALID</code>
+     * @param oldValue the old value object, or <code>null</code>
+     * @param newValue the new value, or <code>null</code>
+     */
+    protected void fireValueChanged(String property, Object oldValue,
+            Object newValue) {
+        if (propertyChangeListener is null) {
+            return;
+        }
+        propertyChangeListener.propertyChange(new PropertyChangeEvent(this,
+                property, oldValue, newValue));
+    }
+
+    /**
+     * Returns the symbolic font name used by this field editor.
+     *
+     * @return the symbolic font name
+     */
+    public String getFieldEditorFontName() {
+        return JFaceResources.DIALOG_FONT;
+    }
+
+    /**
+     * Returns the label control.
+     *
+     * @return the label control, or <code>null</code>
+     *  if no label control has been created
+     */
+    protected Label getLabelControl() {
+        return label;
+    }
+
+    /**
+     * Returns this field editor's label component.
+     * <p>
+     * The label is created if it does not already exist
+     * </p>
+     *
+     * @param parent the parent
+     * @return the label control
+     */
+    public Label getLabelControl(Composite parent) {
+        if (label is null) {
+            label = new Label(parent, DWT.LEFT);
+            label.setFont(parent.getFont());
+            String text = getLabelText();
+            if (text !is null) {
+                label.setText(text);
+            }
+            label.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    label = null;
+                }
+            });
+        } else {
+            checkParent(label, parent);
+        }
+        return label;
+    }
+
+    /**
+     * Returns this field editor's label text.
+     *
+     * @return the label text
+     */
+    public String getLabelText() {
+        return labelText;
+    }
+
+    /**
+     * Returns the number of basic controls this field editor consists of.
+     *
+     * @return the number of controls
+     */
+    public abstract int getNumberOfControls();
+
+    /**
+     * Returns the name of the preference this field editor operates on.
+     *
+     * @return the name of the preference
+     */
+    public String getPreferenceName() {
+        return preferenceName;
+    }
+
+    /**
+     * Returns the preference page in which this field editor
+     * appears.
+     *
+     * @return the preference page, or <code>null</code> if none
+     * @deprecated use #getPage()
+     */
+    protected PreferencePage getPreferencePage() {
+        if(page !is null && cast(PreferencePage)page ) {
+            return cast(PreferencePage) page;
+        }
+        return null;
+    }
+
+    /**
+     * Return the DialogPage that the receiver is sending
+     * updates to.
+     *
+     * @return DialogPage or <code>null</code> if it
+     * has not been set.
+     *
+     * @since 3.1
+     */
+    protected DialogPage getPage(){
+        return page;
+    }
+
+    /**
+     * Returns the preference store used by this field editor.
+     *
+     * @return the preference store, or <code>null</code> if none
+     * @see #setPreferenceStore
+     */
+    public IPreferenceStore getPreferenceStore() {
+        return preferenceStore;
+    }
+
+    /**
+     * Initialize the field editor with the given preference name and label.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param text the label text of the field editor
+     */
+    protected void init(String name, String text) {
+        Assert.isNotNull(name);
+        Assert.isNotNull(text);
+        preferenceName = name;
+        this.labelText = text;
+    }
+
+    /**
+     * Returns whether this field editor contains a valid value.
+     * <p>
+     * The default implementation of this framework method
+     * returns <code>true</code>. Subclasses wishing to perform
+     * validation should override both this method and
+     * <code>refreshValidState</code>.
+     * </p>
+     *
+     * @return <code>true</code> if the field value is valid,
+     *   and <code>false</code> if invalid
+     * @see #refreshValidState()
+     */
+    public bool isValid() {
+        return true;
+    }
+
+    /**
+     * Initializes this field editor with the preference value from
+     * the preference store.
+     */
+    public void load() {
+        if (preferenceStore !is null) {
+            isDefaultPresented = false;
+            doLoad();
+            refreshValidState();
+        }
+    }
+
+    /**
+     * Initializes this field editor with the default preference value
+     * from the preference store.
+     */
+    public void loadDefault() {
+        if (preferenceStore !is null) {
+            isDefaultPresented = true;
+            doLoadDefault();
+            refreshValidState();
+        }
+    }
+
+    /**
+     * Returns whether this field editor currently presents the
+     * default value for its preference.
+     *
+     * @return <code>true</code> if the default value is presented,
+     *   and <code>false</code> otherwise
+     */
+    public bool presentsDefaultValue() {
+        return isDefaultPresented;
+    }
+
+    /**
+     * Refreshes this field editor's valid state after a value change
+     * and fires an <code>IS_VALID</code> property change event if
+     * warranted.
+     * <p>
+     * The default implementation of this framework method does
+     * nothing. Subclasses wishing to perform validation should override
+     * both this method and <code>isValid</code>.
+     * </p>
+     *
+     * @see #isValid
+     */
+    protected void refreshValidState() {
+    }
+
+    /**
+     * Sets the focus to this field editor.
+     * <p>
+     * The default implementation of this framework method
+     * does nothing. Subclasses may reimplement.
+     * </p>
+     */
+    public void setFocus() {
+        // do nothing;
+    }
+
+    /**
+     * Sets this field editor's label text.
+     * The label is typically presented to the left of the entry field.
+     *
+     * @param text the label text
+     */
+    public void setLabelText(String text) {
+        Assert.isNotNull(text);
+        labelText = text;
+        if (label !is null) {
+            label.setText(text);
+        }
+    }
+
+    /**
+     * Sets the name of the preference this field editor operates on.
+     * <p>
+     * The ability to change this allows the same field editor object
+     * to be reused for different preferences.
+     * </p>
+     * <p>
+     * For example: <p>
+     * <pre>
+     *  ...
+     *  editor.setPreferenceName("font");
+     *  editor.load();
+     * </pre>
+     * </p>
+     *
+     * @param name the name of the preference
+     */
+    public void setPreferenceName(String name) {
+        preferenceName = name;
+    }
+
+    /**
+     * Sets the preference page in which this field editor
+     * appears.
+     *
+     * @param preferencePage the preference page, or <code>null</code> if none
+     * @deprecated use #setPage(DialogPage)
+     */
+    public void setPreferencePage(PreferencePage preferencePage) {
+        setPage(preferencePage);
+    }
+
+
+    /**
+     * Set the page to be the receiver.
+     * @param dialogPage
+     *
+     * @since 3.1
+     */
+    public void setPage(DialogPage dialogPage) {
+        page = dialogPage;
+
+    }
+
+    /**
+     * Sets the preference store used by this field editor.
+     *
+     * @param store the preference store, or <code>null</code> if none
+     * @see #getPreferenceStore
+     */
+    public void setPreferenceStore(IPreferenceStore store) {
+        preferenceStore = store;
+    }
+
+    /**
+     * Sets whether this field editor is presenting the default value.
+     *
+     * @param booleanValue <code>true</code> if the default value is being presented,
+     *  and <code>false</code> otherwise
+     */
+    protected void setPresentsDefaultValue(bool booleanValue) {
+        isDefaultPresented = booleanValue;
+    }
+    package void setPresentsDefaultValue_package(bool booleanValue) {
+        setPresentsDefaultValue(booleanValue);
+    }
+
+    /**
+     * Sets or removes the property change listener for this field editor.
+     * <p>
+     * Note that field editors can support only a single listener.
+     * </p>
+     *
+     * @param listener a property change listener, or <code>null</code>
+     *  to remove
+     */
+    public void setPropertyChangeListener(IPropertyChangeListener listener) {
+        propertyChangeListener = listener;
+    }
+
+    /**
+     * Shows the given error message in the page for this
+     * field editor if it has one.
+     *
+     * @param msg the error message
+     */
+    protected void showErrorMessage(String msg) {
+        if (page !is null) {
+            page.setErrorMessage(msg);
+        }
+    }
+
+    /**
+     * Shows the given message in the page for this
+     * field editor if it has one.
+     *
+     * @param msg the message
+     */
+    protected void showMessage(String msg) {
+        if (page !is null) {
+            page.setErrorMessage(msg);
+        }
+    }
+
+    /**
+     * Stores this field editor's value back into the preference store.
+     */
+    public void store() {
+        if (preferenceStore is null) {
+            return;
+        }
+
+        if (isDefaultPresented) {
+            preferenceStore.setToDefault(preferenceName);
+        } else {
+            doStore();
+        }
+    }
+
+    /**
+     * Set the GridData on button to be one that is spaced for the
+     * current font.
+     * @param button the button the data is being set on.
+     */
+
+    protected void setButtonLayoutData(Button button) {
+
+        GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+
+        // Compute and store a font metric
+        GC gc = new GC(button);
+        gc.setFont(button.getFont());
+        FontMetrics fontMetrics = gc.getFontMetrics();
+        gc.dispose();
+
+        int widthHint = dwtx.jface.dialogs.Dialog.Dialog
+                .convertVerticalDLUsToPixels(fontMetrics,
+                        IDialogConstants.BUTTON_WIDTH);
+        data.widthHint = Math.max(widthHint, button.computeSize(DWT.DEFAULT,
+                DWT.DEFAULT, true).x);
+        button.setLayoutData(data);
+    }
+
+    /**
+     * Set whether or not the controls in the field editor
+     * are enabled.
+     * @param enabled The enabled state.
+     * @param parent The parent of the controls in the group.
+     *  Used to create the controls if required.
+     */
+    public void setEnabled(bool enabled, Composite parent) {
+        getLabelControl(parent).setEnabled(enabled);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/FieldEditorPreferencePage.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,382 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Chris Tilt (chris@tilts.net) - Bug 38547 - [Preferences] Changing preferences
+ *          ignored after "Restore defaults" pressed.
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.preference.FieldEditorPreferencePage;
+
+import dwtx.jface.preference.PreferencePage;
+import dwtx.jface.preference.FieldEditor;
+import dwtx.jface.preference.FieldEditorPreferencePage;
+
+import tango.util.collection.ArraySeq;
+import tango.util.collection.model.Seq;
+// import java.util.Iterator;
+// import java.util.List;
+
+import dwt.DWT;
+import dwt.layout.GridData;
+import dwt.layout.GridLayout;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwtx.jface.resource.ImageDescriptor;
+import dwtx.jface.util.IPropertyChangeListener;
+import dwtx.jface.util.PropertyChangeEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A special abstract preference page to host field editors.
+ * <p>
+ * Subclasses must implement the <code>createFieldEditors</code> method
+ * and should override <code>createLayout</code> if a special layout of the field
+ * editors is needed.
+ * </p>
+ */
+public abstract class FieldEditorPreferencePage : PreferencePage,
+        IPropertyChangeListener {
+
+    /**
+     * Layout constant (value <code>0</code>) indicating that
+     * each field editor is handled as a single component.
+     */
+    public static const int FLAT = 0;
+
+    /**
+     * Layout constant (value <code>1</code>) indicating that
+     * the field editors' basic controls are put into a grid layout.
+     */
+    public static const int GRID = 1;
+
+    /**
+     * The vertical spacing used by layout styles <code>FLAT</code>
+     * and <code>GRID</code>.
+     */
+    protected static const int VERTICAL_SPACING = 10;
+
+    /**
+     * The margin width used by layout styles <code>FLAT</code>
+     * and <code>GRID</code>.
+     */
+    protected static const int MARGIN_WIDTH = 0;
+
+    /**
+     * The margin height used by layout styles <code>FLAT</code>
+     * and <code>GRID</code>.
+     */
+    protected static const int MARGIN_HEIGHT = 0;
+
+    /**
+     * The field editors, or <code>null</code> if not created yet.
+     */
+    private Seq!(Object) fields = null;
+
+    /**
+     * The layout style; either <code>FLAT</code> or <code>GRID</code>.
+     */
+    private int style;
+
+    /**
+     * The first invalid field editor, or <code>null</code>
+     * if all field editors are valid.
+     */
+    private FieldEditor invalidFieldEditor = null;
+
+    /**
+     * The parent composite for field editors
+     */
+    private Composite fieldEditorParent;
+
+    /**
+     * Create a new instance of the reciever.
+     */
+    public this() {
+        this(FLAT);
+    }
+
+    /**
+     * Creates a new field editor preference page with the given style,
+     * an empty title, and no image.
+     *
+     * @param style either <code>GRID</code> or <code>FLAT</code>
+     */
+    protected this(int style) {
+        super();
+        this.style = style;
+    }
+
+    /**
+     * Creates a new field editor preference page with the given title
+     * and style, but no image.
+     *
+     * @param title the title of this preference page
+     * @param style either <code>GRID</code> or <code>FLAT</code>
+     */
+    protected this(String title, int style) {
+        super(title);
+        this.style = style;
+    }
+
+    /**
+     * Creates a new field editor preference page with the given title,
+     * image, and style.
+     *
+     * @param title the title of this preference page
+     * @param image the image for this preference page, or
+     *   <code>null</code> if none
+     * @param style either <code>GRID</code> or <code>FLAT</code>
+     */
+    protected this(String title, ImageDescriptor image,
+            int style) {
+        super(title, image);
+        this.style = style;
+    }
+
+    /**
+     * Adds the given field editor to this page.
+     *
+     * @param editor the field editor
+     */
+    protected void addField(FieldEditor editor) {
+        if (fields is null) {
+            fields = new ArraySeq!(Object);
+        }
+        fields.append(editor);
+    }
+
+    /**
+     * Adjust the layout of the field editors so that
+     * they are properly aligned.
+     */
+    protected void adjustGridLayout() {
+        int numColumns = calcNumberOfColumns();
+        (cast(GridLayout) fieldEditorParent.getLayout()).numColumns = numColumns;
+        if (fields !is null) {
+            for (int i = 0; i < fields.size(); i++) {
+                FieldEditor fieldEditor = cast(FieldEditor) fields.get(i);
+                fieldEditor.adjustForNumColumns_package(numColumns);
+            }
+        }
+    }
+
+    /**
+     * Applys the font to the field editors managed by this page.
+     */
+    protected void applyFont() {
+        if (fields !is null) {
+            foreach( e; fields ){
+                FieldEditor pe = cast(FieldEditor) e;
+                pe.applyFont_package();
+            }
+        }
+    }
+
+    /**
+     * Calculates the number of columns needed to host all field editors.
+     *
+     * @return the number of columns
+     */
+    private int calcNumberOfColumns() {
+        int result = 0;
+        if (fields !is null) {
+            foreach( e; fields ){
+                FieldEditor pe = cast(FieldEditor) e;
+                result = Math.max(result, pe.getNumberOfControls());
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Recomputes the page's error state by calling <code>isValid</code> for
+     * every field editor.
+     */
+    protected void checkState() {
+        bool valid = true;
+        invalidFieldEditor = null;
+        // The state can only be set to true if all
+        // field editors contain a valid value. So we must check them all
+        if (fields !is null) {
+            int size = fields.size();
+            for (int i = 0; i < size; i++) {
+                FieldEditor editor = cast(FieldEditor) fields.get(i);
+                valid = valid && editor.isValid();
+                if (!valid) {
+                    invalidFieldEditor = editor;
+                    break;
+                }
+            }
+        }
+        setValid(valid);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on PreferencePage.
+     */
+    protected Control createContents(Composite parent) {
+        fieldEditorParent = new Composite(parent, DWT.NULL);
+        GridLayout layout = new GridLayout();
+        layout.numColumns = 1;
+        layout.marginHeight = 0;
+        layout.marginWidth = 0;
+        fieldEditorParent.setLayout(layout);
+        fieldEditorParent.setFont(parent.getFont());
+
+        createFieldEditors();
+
+        if (style is GRID) {
+            adjustGridLayout();
+        }
+
+        initialize();
+        checkState();
+        return fieldEditorParent;
+    }
+
+    /**
+     * Creates the page's field editors.
+     * <p>
+     * The default implementation of this framework method
+     * does nothing. Subclass must implement this method to
+     * create the field editors.
+     * </p>
+     * <p>
+     * Subclasses should call <code>getFieldEditorParent</code>
+     * to obtain the parent control for each field editor.
+     * This same parent should not be used for more than
+     * one editor as the parent may change for each field
+     * editor depending on the layout style of the page
+     * </p>
+     */
+    protected abstract void createFieldEditors();
+
+    /**
+     * The field editor preference page implementation of an <code>IDialogPage</code>
+     * method disposes of this page's controls and images.
+     * Subclasses may override to release their own allocated DWT
+     * resources, but must call <code>super.dispose</code>.
+     */
+    public void dispose() {
+        super.dispose();
+        if (fields !is null) {
+            foreach( e; fields ){
+                FieldEditor pe = cast(FieldEditor) e;
+                pe.setPage(null);
+                pe.setPropertyChangeListener(null);
+                pe.setPreferenceStore(null);
+            }
+        }
+    }
+
+    /**
+     * Returns a parent composite for a field editor.
+     * <p>
+     * This value must not be cached since a new parent
+     * may be created each time this method called. Thus
+     * this method must be called each time a field editor
+     * is constructed.
+     * </p>
+     *
+     * @return a parent
+     */
+    protected Composite getFieldEditorParent() {
+        if (style is FLAT) {
+            // Create a new parent for each field editor
+            Composite parent = new Composite(fieldEditorParent, DWT.NULL);
+            parent.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+            return parent;
+        }
+        // Just return the parent
+        return fieldEditorParent;
+    }
+
+    /**
+     * Initializes all field editors.
+     */
+    protected void initialize() {
+        if (fields !is null) {
+            foreach( e; fields ){
+                FieldEditor pe = cast(FieldEditor) e;
+                pe.setPage(this);
+                pe.setPropertyChangeListener(this);
+                pe.setPreferenceStore(getPreferenceStore());
+                pe.load();
+            }
+        }
+    }
+
+    /**
+     * The field editor preference page implementation of a <code>PreferencePage</code>
+     * method loads all the field editors with their default values.
+     */
+    protected void performDefaults() {
+        if (fields !is null) {
+            foreach( e; fields ){
+                FieldEditor pe = cast(FieldEditor) e;
+                pe.loadDefault();
+            }
+        }
+        // Force a recalculation of my error state.
+        checkState();
+        super.performDefaults();
+    }
+
+    /**
+     * The field editor preference page implementation of this
+     * <code>PreferencePage</code> method saves all field editors by
+     * calling <code>FieldEditor.store</code>. Note that this method
+     * does not save the preference store itself; it just stores the
+     * values back into the preference store.
+     *
+     * @see FieldEditor#store()
+     */
+    public bool performOk() {
+        if (fields !is null) {
+            foreach( e; fields ){
+                FieldEditor pe = cast(FieldEditor) e;
+                pe.store();
+                pe.setPresentsDefaultValue_package(false);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * The field editor preference page implementation of this <code>IPreferencePage</code>
+     * (and <code>IPropertyChangeListener</code>) method intercepts <code>IS_VALID</code>
+     * events but passes other events on to its superclass.
+     */
+    public void propertyChange(PropertyChangeEvent event) {
+
+        if (event.getProperty().equals(FieldEditor.IS_VALID)) {
+            bool newValue = (cast(ValueWrapperBool) event.getNewValue()).value;
+            // If the new value is true then we must check all field editors.
+            // If it is false, then the page is invalid in any case.
+            if (newValue) {
+                checkState();
+            } else {
+                invalidFieldEditor = cast(FieldEditor) event.getSource();
+                setValid(newValue);
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IDialog.
+     */
+    public void setVisible(bool visible) {
+        super.setVisible(visible);
+        if (visible && invalidFieldEditor !is null) {
+            invalidFieldEditor.setFocus();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/FileFieldEditor.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,174 @@
+/*******************************************************************************
+ * 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.preference.FileFieldEditor;
+
+import dwtx.jface.preference.StringButtonFieldEditor;
+// import java.io.File;
+
+import dwt.DWT;
+import dwt.widgets.Composite;
+import dwt.widgets.FileDialog;
+import dwtx.jface.resource.JFaceResources;
+
+import dwt.dwthelper.utils;
+import tango.io.FilePath;
+import tango.io.FileSystem;
+
+/**
+ * A field editor for a file path type preference. A standard file
+ * dialog appears when the user presses the change button.
+ */
+public class FileFieldEditor : StringButtonFieldEditor {
+
+    /**
+     * List of legal file extension suffixes, or <code>null</code>
+     * for system defaults.
+     */
+    private String[] extensions = null;
+
+    /**
+     * Indicates whether the path must be absolute;
+     * <code>false</code> by default.
+     */
+    private bool enforceAbsolute = false;
+
+    /**
+     * Creates a new file field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a file field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText, Composite parent) {
+        this(name, labelText, false, parent);
+    }
+
+    /**
+     * Creates a file field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param enforceAbsolute <code>true</code> if the file path
+     *  must be absolute, and <code>false</code> otherwise
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText,
+            bool enforceAbsolute, Composite parent) {
+        init(name, labelText);
+        this.enforceAbsolute = enforceAbsolute;
+        setErrorMessage(JFaceResources
+                .getString("FileFieldEditor.errorMessage"));//$NON-NLS-1$
+        setChangeButtonText(JFaceResources.getString("openBrowse"));//$NON-NLS-1$
+        setValidateStrategy(VALIDATE_ON_FOCUS_LOST);
+        createControl(parent);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StringButtonFieldEditor.
+     * Opens the file chooser dialog and returns the selected file.
+     */
+    protected String changePressed() {
+        auto f = new FilePath(getTextControl().getText());
+        if (!f.exists()) {
+            f = cast(FilePath)null;
+        }
+        auto d = getFile(f);
+        if (d is null) {
+            return null;
+        }
+
+        return FileSystem.toAbsolute( d ).toString();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StringFieldEditor.
+     * Checks whether the text input field specifies an existing file.
+     */
+    protected bool checkState() {
+
+        String msg = null;
+
+        String path = getTextControl().getText();
+        if (path !is null) {
+            path = path.trim();
+        } else {
+            path = "";//$NON-NLS-1$
+        }
+        if (path.length is 0) {
+            if (!isEmptyStringAllowed()) {
+                msg = getErrorMessage();
+            }
+        } else {
+            auto file = new FilePath(path);
+            if (/+file.isFile()+/ file.exists && !file.isFolder ) {
+                if (enforceAbsolute && !file.isAbsolute()) {
+                    msg = JFaceResources
+                            .getString("FileFieldEditor.errorMessage2");//$NON-NLS-1$
+                }
+            } else {
+                msg = getErrorMessage();
+            }
+        }
+
+        if (msg !is null) { // error
+            showErrorMessage(msg);
+            return false;
+        }
+
+        // OK!
+        clearErrorMessage();
+        return true;
+    }
+
+    /**
+     * Helper to open the file chooser dialog.
+     * @param startingDirectory the directory to open the dialog on.
+     * @return File The File the user selected or <code>null</code> if they
+     * do not.
+     */
+    private FilePath getFile(FilePath startingDirectory) {
+
+        FileDialog dialog = new FileDialog(getShell(), DWT.OPEN);
+        if (startingDirectory !is null) {
+            dialog.setFileName(startingDirectory.path());
+        }
+        if (extensions !is null) {
+            dialog.setFilterExtensions(extensions);
+        }
+        String file = dialog.open();
+        if (file !is null) {
+            file = file.trim();
+            if (file.length > 0) {
+                return new FilePath(file);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Sets this file field editor's file extension filter.
+     *
+     * @param extensions a list of file extension, or <code>null</code>
+     * to set the filter to the system's default value
+     */
+    public void setFileExtensions(String[] extensions) {
+        this.extensions = extensions;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/FontFieldEditor.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,427 @@
+/*******************************************************************************
+ * 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.preference.FontFieldEditor;
+
+import dwtx.jface.preference.FieldEditor;
+import dwtx.jface.preference.PreferenceConverter;
+
+import dwt.DWT;
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.events.SelectionAdapter;
+import dwt.events.SelectionEvent;
+import dwt.graphics.Font;
+import dwt.graphics.FontData;
+import dwt.layout.GridData;
+import dwt.widgets.Button;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.FontDialog;
+import dwt.widgets.Label;
+import dwt.widgets.Text;
+import dwtx.core.runtime.Assert;
+import dwtx.jface.dialogs.IDialogConstants;
+import dwtx.jface.resource.JFaceResources;
+import dwtx.jface.resource.StringConverter;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A field editor for a font type preference.
+ */
+public class FontFieldEditor : FieldEditor {
+
+    /**
+     * The change font button, or <code>null</code> if none
+     * (before creation and after disposal).
+     */
+    private Button changeFontButton = null;
+
+    /**
+     * The text for the change font button, or <code>null</code>
+     * if missing.
+     */
+    private String changeButtonText;
+
+    /**
+     * The text for the preview, or <code>null</code> if no preview is desired
+     */
+    private String previewText;
+
+    /**
+     * Font data for the chosen font button, or <code>null</code> if none.
+     */
+    private FontData[] chosenFont;
+
+    /**
+     * The label that displays the selected font, or <code>null</code> if none.
+     */
+    private Label valueControl;
+
+    /**
+     * The previewer, or <code>null</code> if none.
+     */
+    private DefaultPreviewer previewer;
+
+    /**
+     * Internal font previewer implementation.
+     */
+    private static class DefaultPreviewer {
+        private Text text;
+
+        private String string;
+
+        private Font font;
+
+        /**
+         * Constructor for the previewer.
+         * @param s
+         * @param parent
+         */
+        public this(String s, Composite parent) {
+            string = s;
+            text = new Text(parent, DWT.READ_ONLY | DWT.BORDER);
+            text.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent e) {
+                    if (font !is null) {
+                        font.dispose();
+                    }
+                }
+            });
+            if (string !is null) {
+                text.setText(string);
+            }
+        }
+
+        /**
+         * @return the control the previewer is using
+         */
+        public Control getControl() {
+            return text;
+        }
+
+        /**
+         * Set the font to display with
+         * @param fontData
+         */
+        public void setFont(FontData[] fontData) {
+            if (font !is null) {
+                font.dispose();
+            }
+            font = new Font(text.getDisplay(), fontData);
+            text.setFont(font);
+        }
+
+        /**
+         * @return the preferred size of the previewer.
+         */
+        public int getPreferredExtent() {
+            return 40;
+        }
+    }
+
+    /**
+     * Creates a new font field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a font field editor with an optional preview area.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param previewAreaText the text used for the preview window. If it is
+     * <code>null</code> there will be no preview area,
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText,
+            String previewAreaText, Composite parent) {
+        init(name, labelText);
+        previewText = previewAreaText;
+        changeButtonText = JFaceResources.getString("openChange"); //$NON-NLS-1$
+        createControl(parent);
+
+    }
+
+    /**
+     * Creates a font field editor without a preview.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText, Composite parent) {
+        this(name, labelText, null, parent);
+
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void adjustForNumColumns(int numColumns) {
+
+        GridData data = new GridData();
+        if (valueControl.getLayoutData() !is null) {
+            data = cast(GridData) valueControl.getLayoutData();
+        }
+
+        data.horizontalSpan = numColumns - getNumberOfControls() + 1;
+        valueControl.setLayoutData(data);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void applyFont() {
+        if (chosenFont !is null && previewer !is null) {
+            previewer.setFont(chosenFont);
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doFillIntoGrid(Composite parent, int numColumns) {
+        getLabelControl(parent);
+
+        valueControl = getValueControl(parent);
+
+        GridData gd = new GridData(GridData.FILL_HORIZONTAL
+                | GridData.GRAB_HORIZONTAL);
+        gd.horizontalSpan = numColumns - getNumberOfControls() + 1;
+        valueControl.setLayoutData(gd);
+        if (previewText !is null) {
+            previewer = new DefaultPreviewer(previewText, parent);
+            gd = new GridData(GridData.FILL_HORIZONTAL);
+            gd.heightHint = previewer.getPreferredExtent();
+            gd.widthHint = previewer.getPreferredExtent();
+            previewer.getControl().setLayoutData(gd);
+        }
+
+        changeFontButton = getChangeControl(parent);
+        gd = new GridData();
+        int widthHint = convertHorizontalDLUsToPixels(changeFontButton,
+                IDialogConstants.BUTTON_WIDTH);
+        gd.widthHint = Math.max(widthHint, changeFontButton.computeSize(
+                DWT.DEFAULT, DWT.DEFAULT, true).x);
+        changeFontButton.setLayoutData(gd);
+
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doLoad() {
+        if (changeFontButton is null) {
+            return;
+        }
+        updateFont(PreferenceConverter.getFontDataArray(getPreferenceStore(),
+                getPreferenceName()));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doLoadDefault() {
+        if (changeFontButton is null) {
+            return;
+        }
+        updateFont(PreferenceConverter.getDefaultFontDataArray(
+                getPreferenceStore(), getPreferenceName()));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doStore() {
+        if (chosenFont !is null) {
+            PreferenceConverter.setValue(getPreferenceStore(),
+                    getPreferenceName(), chosenFont);
+        }
+    }
+
+    /**
+     * Returns the change button for this field editor.
+     *
+     * @param parent The Composite to create the button in if required.
+     * @return the change button
+     */
+    protected Button getChangeControl(Composite parent) {
+        if (changeFontButton is null) {
+            changeFontButton = new Button(parent, DWT.PUSH);
+            if (changeButtonText !is null) {
+                changeFontButton.setText(changeButtonText);
+            }
+            changeFontButton.addSelectionListener(new class SelectionAdapter {
+                public void widgetSelected(SelectionEvent event) {
+                    FontDialog fontDialog = new FontDialog(changeFontButton
+                            .getShell());
+                    if (chosenFont !is null) {
+                        fontDialog.setFontList(chosenFont);
+                    }
+                    FontData font = fontDialog.open();
+                    if (font !is null) {
+                        FontData[] oldFont = chosenFont;
+                        if (oldFont is null) {
+                            oldFont = JFaceResources.getDefaultFont()
+                                    .getFontData();
+                        }
+                        setPresentsDefaultValue(false);
+                        FontData[] newData = new FontData[1];
+                        newData[0] = font;
+                        updateFont(newData);
+                        fireValueChanged(VALUE, oldFont[0], font);
+                    }
+
+                }
+            });
+            changeFontButton.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    changeFontButton = null;
+                }
+            });
+            changeFontButton.setFont(parent.getFont());
+            setButtonLayoutData(changeFontButton);
+        } else {
+            checkParent(changeFontButton, parent);
+        }
+        return changeFontButton;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public int getNumberOfControls() {
+        if (previewer is null) {
+            return 3;
+        }
+
+        return 4;
+    }
+
+    /**
+     * Returns the preferred preview height.
+     *
+     * @return the height, or <code>-1</code> if no previewer
+     *  is installed
+     */
+    public int getPreferredPreviewHeight() {
+        if (previewer is null) {
+            return -1;
+        }
+        return previewer.getPreferredExtent();
+    }
+
+    /**
+     * Returns the preview control for this field editor.
+     *
+     * @return the preview control
+     */
+    public Control getPreviewControl() {
+        if (previewer is null) {
+            return null;
+        }
+
+        return previewer.getControl();
+    }
+
+    /**
+     * Returns the value control for this field editor. The value control
+     * displays the currently selected font name.
+     * @param parent The Composite to create the viewer in if required
+     * @return the value control
+     */
+    protected Label getValueControl(Composite parent) {
+        if (valueControl is null) {
+            valueControl = new Label(parent, DWT.LEFT);
+            valueControl.setFont(parent.getFont());
+            valueControl.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    valueControl = null;
+                }
+            });
+        } else {
+            checkParent(valueControl, parent);
+        }
+        return valueControl;
+    }
+
+    /**
+     * Sets the text of the change button.
+     *
+     * @param text the new text
+     */
+    public void setChangeButtonText(String text) {
+        Assert.isNotNull(text);
+        changeButtonText = text;
+        if (changeFontButton !is null) {
+            changeFontButton.setText(text);
+        }
+    }
+
+    /**
+     * Updates the change font button and the previewer to reflect the
+     * newly selected font.
+     * @param font The FontData[] to update with.
+     */
+    private void updateFont(FontData font[]) {
+        FontData[] bestFont = JFaceResources.getFontRegistry().filterData(
+                font, valueControl.getDisplay());
+
+        //if we have nothing valid do as best we can
+        if (bestFont is null) {
+            bestFont = getDefaultFontData();
+        }
+
+        //Now cache this value in the receiver
+        this.chosenFont = bestFont;
+
+        if (valueControl !is null) {
+            valueControl.setText(StringConverter.asString(chosenFont[0]));
+        }
+        if (previewer !is null) {
+            previewer.setFont(bestFont);
+        }
+    }
+
+    /**
+     * Store the default preference for the field
+     * being edited
+     */
+    protected void setToDefault() {
+        FontData[] defaultFontData = PreferenceConverter
+                .getDefaultFontDataArray(getPreferenceStore(),
+                        getPreferenceName());
+        PreferenceConverter.setValue(getPreferenceStore(), getPreferenceName(),
+                defaultFontData);
+    }
+
+    /**
+     * Get the system default font data.
+     * @return FontData[]
+     */
+    private FontData[] getDefaultFontData() {
+        return valueControl.getDisplay().getSystemFont().getFontData();
+    }
+
+    /*
+     * @see FieldEditor.setEnabled(bool,Composite).
+     */
+    public void setEnabled(bool enabled, Composite parent) {
+        super.setEnabled(enabled, parent);
+        getChangeControl(parent).setEnabled(enabled);
+        getValueControl(parent).setEnabled(enabled);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/IPersistentPreferenceStore.d	Sat Apr 05 01:45:47 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.preference.IPersistentPreferenceStore;
+
+import dwtx.jface.preference.IPreferenceStore;
+
+/**
+ * IPersistentPreferenceStore is a preference store that can
+ * be saved.
+ */
+public interface IPersistentPreferenceStore : IPreferenceStore {
+
+    /**
+     * Saves the non-default-valued preferences known to this preference
+     * store to the file from which they were originally loaded.
+     *
+     * @exception java.io.IOException if there is a problem saving this store
+     */
+    public void save();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/IPreferenceNode.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.preference.IPreferenceNode;
+
+import dwtx.jface.preference.IPreferencePage;
+
+import dwt.graphics.Image;
+import dwt.dwthelper.utils;
+
+/**
+ * Interface to a node in a preference dialog.
+ * A preference node maintains a label and image used to display the
+ * node in a preference dialog (usually in the form of a tree),
+ * as well as the preference page this node stands for.
+ *
+ * The node may use lazy creation for its page
+ *
+ * Note that all preference nodes must be dispose their resources.
+ * The node must dispose the page managed by this node, and any DWT resources
+ * allocated by this node (Images, Fonts, etc).
+ * However the node itself may be reused.
+ */
+public interface IPreferenceNode {
+    /**
+     * Adds the given preference node as a subnode of this
+     * preference node.
+     *
+     * @param node the node to add
+     */
+    public void add(IPreferenceNode node);
+
+    /**
+     * Creates the preference page for this node.
+     */
+    public void createPage();
+
+    /**
+     * Release the page managed by this node, and any DWT resources
+     * held onto by this node (Images, Fonts, etc).
+     *
+     * Note that nodes are reused so this is not a call to dispose the
+     * node itself.
+     */
+    public void disposeResources();
+
+    /**
+     * Returns the subnode of this contribution node with the given node id.
+     *
+     * @param id the preference node id
+     * @return the subnode, or <code>null</code> if none
+     */
+    public IPreferenceNode findSubNode(String id);
+
+    /**
+     * Returns the id of this contribution node.
+     * This id identifies a contribution node relative to its parent.
+     *
+     * @return the node id
+     */
+    public String getId();
+
+    /**
+     * Returns the image used to present this node in a preference dialog.
+     *
+     * @return the image for this node, or <code>null</code>
+     *   if there is no image for this node
+     */
+    public Image getLabelImage();
+
+    /**
+     * Returns the text label used to present this node in a preference dialog.
+     *
+     * @return the text label for this node, or <code>null</code>
+     *   if there is no label for this node
+     */
+    public String getLabelText();
+
+    /**
+     * Returns the preference page for this node.
+     *
+     * @return the preference page
+     */
+    public IPreferencePage getPage();
+
+    /**
+     * Returns an iterator over the subnodes (immediate children)
+     * of this contribution node.
+     *
+     * @return an IPreferenceNode array containing the child nodes
+     */
+    public IPreferenceNode[] getSubNodes();
+
+    /**
+     * Removes the subnode of this preference node with the given node id.
+     *
+     * @param id the subnode id
+     * @return the removed subnode, or <code>null</code> if none
+     */
+    public IPreferenceNode remove(String id);
+
+    /**
+     * Removes the given preference node from the list of subnodes
+     * (immediate children) of this node.
+     *
+     * @param node the node to remove
+     * @return <code>true</code> if the node was removed,
+     *  and <code>false</code> otherwise
+     */
+    public bool remove(IPreferenceNode node);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/IPreferencePage.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * 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.preference.IPreferencePage;
+
+import dwtx.jface.preference.IPreferencePageContainer;
+
+import dwt.graphics.Point;
+import dwtx.jface.dialogs.IDialogPage;
+
+/**
+ * An interface for a preference page. This interface
+ * is used primarily by the page's container
+ */
+public interface IPreferencePage : IDialogPage {
+
+    /**
+     * Computes a size for this page's UI component.
+     *
+     * @return the size of the preference page encoded as
+     *   <code>new Point(width,height)</code>, or
+     *   <code>(0,0)</code> if the page doesn't currently have any UI component
+     */
+    public Point computeSize();
+
+    /**
+     * Returns whether this dialog page is in a valid state.
+     *
+     * @return <code>true</code> if the page is in a valid state,
+     *   and <code>false</code> if invalid
+     */
+    public bool isValid();
+
+    /**
+     * Checks whether it is alright to leave this page.
+     *
+     * @return <code>false</code> to abort page flipping and the
+     *  have the current page remain visible, and <code>true</code>
+     *  to allow the page flip
+     */
+    public bool okToLeave();
+
+    /**
+     * Notifies that the container of this preference page has been canceled.
+     *
+     * @return <code>false</code> to abort the container's cancel
+     *  procedure and <code>true</code> to allow the cancel to happen
+     */
+    public bool performCancel();
+
+    /**
+     * Notifies that the OK button of this page's container has been pressed.
+     *
+     * @return <code>false</code> to abort the container's OK
+     *  processing and <code>true</code> to allow the OK to happen
+     */
+    public bool performOk();
+
+    /**
+     * Sets or clears the container of this page.
+     *
+     * @param preferencePageContainer the preference page container, or <code>null</code>
+     */
+    public void setContainer(IPreferencePageContainer preferencePageContainer);
+
+    /**
+     * Sets the size of this page's UI component.
+     *
+     * @param size the size of the preference page encoded as
+     *   <code>new Point(width,height)</code>
+     */
+    public void setSize(Point size);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/IPreferencePageContainer.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.preference.IPreferencePageContainer;
+
+import dwtx.jface.preference.IPreferenceStore;
+
+import dwt.dwthelper.utils;
+
+/**
+ * An interface used by a preference page to talk to
+ * its dialog.
+ */
+public interface IPreferencePageContainer {
+    /**
+     * Returns the preference store.
+     *
+     * @return the preference store, or <code>null</code> if none
+     */
+    public IPreferenceStore getPreferenceStore();
+
+    /**
+     * Adjusts the enable state of the OK
+     * button to reflect the state of the currently active
+     * page in this container.
+     * <p>
+     * This method is called by the container itself
+     * when its preference page changes and may be called
+     * by the page at other times to force a button state
+     * update.
+     * </p>
+     */
+    public void updateButtons();
+
+    /**
+     * Updates the message (or error message) shown in the message line to
+     * reflect the state of the currently active page in this container.
+     * <p>
+     * This method is called by the container itself
+     * when its preference page changes and may be called
+     * by the page at other times to force a message
+     * update.
+     * </p>
+     */
+    public void updateMessage();
+
+    /**
+     * Updates the title to reflect the state of the
+     * currently active page in this container.
+     * <p>
+     * This method is called by the container itself
+     * when its page changes and may be called
+     * by the page at other times to force a title
+     * update.
+     * </p>
+     */
+    public void updateTitle();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/IntegerFieldEditor.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * 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.preference.IntegerFieldEditor;
+
+import dwtx.jface.preference.StringFieldEditor;
+
+import dwt.widgets.Composite;
+import dwt.widgets.Text;
+import dwtx.jface.resource.JFaceResources;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A field editor for an integer type preference.
+ */
+public class IntegerFieldEditor : StringFieldEditor {
+    private int minValidValue = 0;
+
+    private int maxValidValue = int.max;
+
+    private static const int DEFAULT_TEXT_LIMIT = 10;
+
+    /**
+     * Creates a new integer field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates an integer field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText, Composite parent) {
+        this(name, labelText, parent, DEFAULT_TEXT_LIMIT);
+    }
+
+    /**
+     * Creates an integer field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     * @param textLimit the maximum number of characters in the text.
+     */
+    public this(String name, String labelText, Composite parent,
+            int textLimit) {
+        init(name, labelText);
+        setTextLimit(textLimit);
+        setEmptyStringAllowed(false);
+        setErrorMessage(JFaceResources
+                .getString("IntegerFieldEditor.errorMessage"));//$NON-NLS-1$
+        createControl(parent);
+    }
+
+    /**
+     * Sets the range of valid values for this field.
+     *
+     * @param min the minimum allowed value (inclusive)
+     * @param max the maximum allowed value (inclusive)
+     */
+    public void setValidRange(int min, int max) {
+        minValidValue = min;
+        maxValidValue = max;
+        setErrorMessage(JFaceResources.format(
+                "IntegerFieldEditor.errorMessageRange", //$NON-NLS-1$
+                [ new Integer(min), new Integer(max) ]));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on StringFieldEditor.
+     * Checks whether the entered String is a valid integer or not.
+     */
+    protected bool checkState() {
+
+        Text text = getTextControl();
+
+        if (text is null) {
+            return false;
+        }
+
+        String numberString = text.getText();
+        try {
+            int number = Integer.valueOf(numberString).intValue();
+            if (number >= minValidValue && number <= maxValidValue) {
+                clearErrorMessage();
+                return true;
+            }
+
+            showErrorMessage();
+            return false;
+
+        } catch (NumberFormatException e1) {
+            showErrorMessage();
+        }
+
+        return false;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doLoad() {
+        Text text = getTextControl();
+        if (text !is null) {
+            int value = getPreferenceStore().getInt(getPreferenceName());
+            text.setText( tango.text.convert.Integer.toString(value));//$NON-NLS-1$
+        }
+
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doLoadDefault() {
+        Text text = getTextControl();
+        if (text !is null) {
+            int value = getPreferenceStore().getDefaultInt(getPreferenceName());
+            text.setText(tango.text.convert.Integer.toString( value));//$NON-NLS-1$
+        }
+        valueChanged();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doStore() {
+        Text text = getTextControl();
+        if (text !is null) {
+            Integer i = new Integer(text.getText());
+            getPreferenceStore().setValue(getPreferenceName(), i.intValue());
+        }
+    }
+
+    /**
+     * Returns this field editor's current value as an integer.
+     *
+     * @return the value
+     * @exception NumberFormatException if the <code>String</code> does not
+     *   contain a parsable integer
+     */
+    public int getIntValue() {
+        return (new Integer(getStringValue())).intValue();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/ListEditor.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,454 @@
+/*******************************************************************************
+ * 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.preference.ListEditor;
+
+import dwtx.jface.preference.FieldEditor;
+
+import dwt.DWT;
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.events.SelectionAdapter;
+import dwt.events.SelectionEvent;
+import dwt.events.SelectionListener;
+import dwt.layout.GridData;
+import dwt.layout.GridLayout;
+import dwt.widgets.Button;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.List;
+import dwt.widgets.Shell;
+import dwt.widgets.Widget;
+import dwtx.core.runtime.Assert;
+import dwtx.jface.dialogs.IDialogConstants;
+import dwtx.jface.resource.JFaceResources;
+
+import dwt.dwthelper.utils;
+
+/**
+ * An abstract field editor that manages a list of input values.
+ * The editor displays a list containing the values, buttons for
+ * adding and removing values, and Up and Down buttons to adjust
+ * the order of elements in the list.
+ * <p>
+ * Subclasses must implement the <code>parseString</code>,
+ * <code>createList</code>, and <code>getNewInputObject</code>
+ * framework methods.
+ * </p>
+ */
+public abstract class ListEditor : FieldEditor {
+
+    /**
+     * The list widget; <code>null</code> if none
+     * (before creation or after disposal).
+     */
+    private List list;
+
+    /**
+     * The button box containing the Add, Remove, Up, and Down buttons;
+     * <code>null</code> if none (before creation or after disposal).
+     */
+    private Composite buttonBox;
+
+    /**
+     * The Add button.
+     */
+    private Button addButton;
+
+    /**
+     * The Remove button.
+     */
+    private Button removeButton;
+
+    /**
+     * The Up button.
+     */
+    private Button upButton;
+
+    /**
+     * The Down button.
+     */
+    private Button downButton;
+
+    /**
+     * The selection listener.
+     */
+    private SelectionListener selectionListener;
+
+    /**
+     * Creates a new list field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a list field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    protected this(String name, String labelText, Composite parent) {
+        init(name, labelText);
+        createControl(parent);
+    }
+
+    /**
+     * Notifies that the Add button has been pressed.
+     */
+    private void addPressed() {
+        setPresentsDefaultValue(false);
+        String input = getNewInputObject();
+
+        if (input !is null) {
+            int index = list.getSelectionIndex();
+            if (index >= 0) {
+                list.add(input, index + 1);
+            } else {
+                list.add(input, 0);
+            }
+            selectionChanged();
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void adjustForNumColumns(int numColumns) {
+        Control control = getLabelControl();
+        (cast(GridData) control.getLayoutData()).horizontalSpan = numColumns;
+        (cast(GridData) list.getLayoutData()).horizontalSpan = numColumns - 1;
+    }
+
+    /**
+     * Creates the Add, Remove, Up, and Down button in the given button box.
+     *
+     * @param box the box for the buttons
+     */
+    private void createButtons(Composite box) {
+        addButton = createPushButton(box, "ListEditor.add");//$NON-NLS-1$
+        removeButton = createPushButton(box, "ListEditor.remove");//$NON-NLS-1$
+        upButton = createPushButton(box, "ListEditor.up");//$NON-NLS-1$
+        downButton = createPushButton(box, "ListEditor.down");//$NON-NLS-1$
+    }
+
+    /**
+     * Combines the given list of items into a single string.
+     * This method is the converse of <code>parseString</code>.
+     * <p>
+     * Subclasses must implement this method.
+     * </p>
+     *
+     * @param items the list of items
+     * @return the combined string
+     * @see #parseString
+     */
+    protected abstract String createList(String[] items);
+
+    /**
+     * Helper method to create a push button.
+     *
+     * @param parent the parent control
+     * @param key the resource name used to supply the button's label text
+     * @return Button
+     */
+    private Button createPushButton(Composite parent, String key) {
+        Button button = new Button(parent, DWT.PUSH);
+        button.setText(JFaceResources.getString(key));
+        button.setFont(parent.getFont());
+        GridData data = new GridData(GridData.FILL_HORIZONTAL);
+        int widthHint = convertHorizontalDLUsToPixels(button,
+                IDialogConstants.BUTTON_WIDTH);
+        data.widthHint = Math.max(widthHint, button.computeSize(DWT.DEFAULT,
+                DWT.DEFAULT, true).x);
+        button.setLayoutData(data);
+        button.addSelectionListener(getSelectionListener());
+        return button;
+    }
+
+    /**
+     * Creates a selection listener.
+     */
+    public void createSelectionListener() {
+        selectionListener = new class SelectionAdapter {
+            public void widgetSelected(SelectionEvent event) {
+                Widget widget = event.widget;
+                if (widget is addButton) {
+                    addPressed();
+                } else if (widget is removeButton) {
+                    removePressed();
+                } else if (widget is upButton) {
+                    upPressed();
+                } else if (widget is downButton) {
+                    downPressed();
+                } else if (widget is list) {
+                    selectionChanged();
+                }
+            }
+        };
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doFillIntoGrid(Composite parent, int numColumns) {
+        Control control = getLabelControl(parent);
+        GridData gd = new GridData();
+        gd.horizontalSpan = numColumns;
+        control.setLayoutData(gd);
+
+        list = getListControl(parent);
+        gd = new GridData(GridData.FILL_HORIZONTAL);
+        gd.verticalAlignment = GridData.FILL;
+        gd.horizontalSpan = numColumns - 1;
+        gd.grabExcessHorizontalSpace = true;
+        list.setLayoutData(gd);
+
+        buttonBox = getButtonBoxControl(parent);
+        gd = new GridData();
+        gd.verticalAlignment = GridData.BEGINNING;
+        buttonBox.setLayoutData(gd);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doLoad() {
+        if (list !is null) {
+            String s = getPreferenceStore().getString(getPreferenceName());
+            String[] array = parseString(s);
+            for (int i = 0; i < array.length; i++) {
+                list.add(array[i]);
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doLoadDefault() {
+        if (list !is null) {
+            list.removeAll();
+            String s = getPreferenceStore().getDefaultString(
+                    getPreferenceName());
+            String[] array = parseString(s);
+            for (int i = 0; i < array.length; i++) {
+                list.add(array[i]);
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doStore() {
+        String s = createList(list.getItems());
+        if (s !is null) {
+            getPreferenceStore().setValue(getPreferenceName(), s);
+        }
+    }
+
+    /**
+     * Notifies that the Down button has been pressed.
+     */
+    private void downPressed() {
+        swap(false);
+    }
+
+    /**
+     * Returns this field editor's button box containing the Add, Remove,
+     * Up, and Down button.
+     *
+     * @param parent the parent control
+     * @return the button box
+     */
+    public Composite getButtonBoxControl(Composite parent) {
+        if (buttonBox is null) {
+            buttonBox = new Composite(parent, DWT.NULL);
+            GridLayout layout = new GridLayout();
+            layout.marginWidth = 0;
+            buttonBox.setLayout(layout);
+            createButtons(buttonBox);
+            buttonBox.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    addButton = null;
+                    removeButton = null;
+                    upButton = null;
+                    downButton = null;
+                    buttonBox = null;
+                }
+            });
+
+        } else {
+            checkParent(buttonBox, parent);
+        }
+
+        selectionChanged();
+        return buttonBox;
+    }
+
+    /**
+     * Returns this field editor's list control.
+     *
+     * @param parent the parent control
+     * @return the list control
+     */
+    public List getListControl(Composite parent) {
+        if (list is null) {
+            list = new List(parent, DWT.BORDER | DWT.SINGLE | DWT.V_SCROLL
+                    | DWT.H_SCROLL);
+            list.setFont(parent.getFont());
+            list.addSelectionListener(getSelectionListener());
+            list.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    list = null;
+                }
+            });
+        } else {
+            checkParent(list, parent);
+        }
+        return list;
+    }
+
+    /**
+     * Creates and returns a new item for the list.
+     * <p>
+     * Subclasses must implement this method.
+     * </p>
+     *
+     * @return a new item
+     */
+    protected abstract String getNewInputObject();
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public int getNumberOfControls() {
+        return 2;
+    }
+
+    /**
+     * Returns this field editor's selection listener.
+     * The listener is created if nessessary.
+     *
+     * @return the selection listener
+     */
+    private SelectionListener getSelectionListener() {
+        if (selectionListener is null) {
+            createSelectionListener();
+        }
+        return selectionListener;
+    }
+
+    /**
+     * Returns this field editor's shell.
+     * <p>
+     * This method is internal to the framework; subclassers should not call
+     * this method.
+     * </p>
+     *
+     * @return the shell
+     */
+    protected Shell getShell() {
+        if (addButton is null) {
+            return null;
+        }
+        return addButton.getShell();
+    }
+
+    /**
+     * Splits the given string into a list of strings.
+     * This method is the converse of <code>createList</code>.
+     * <p>
+     * Subclasses must implement this method.
+     * </p>
+     *
+     * @param stringList the string
+     * @return an array of <code>String</code>
+     * @see #createList
+     */
+    protected abstract String[] parseString(String stringList);
+
+    /**
+     * Notifies that the Remove button has been pressed.
+     */
+    private void removePressed() {
+        setPresentsDefaultValue(false);
+        int index = list.getSelectionIndex();
+        if (index >= 0) {
+            list.remove(index);
+            selectionChanged();
+        }
+    }
+
+    /**
+     * Notifies that the list selection has changed.
+     */
+    private void selectionChanged() {
+
+        int index = list.getSelectionIndex();
+        int size = list.getItemCount();
+
+        removeButton.setEnabled(index >= 0);
+        upButton.setEnabled(size > 1 && index > 0);
+        downButton.setEnabled(size > 1 && index >= 0 && index < size - 1);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public void setFocus() {
+        if (list !is null) {
+            list.setFocus();
+        }
+    }
+
+    /**
+     * Moves the currently selected item up or down.
+     *
+     * @param up <code>true</code> if the item should move up,
+     *  and <code>false</code> if it should move down
+     */
+    private void swap(bool up) {
+        setPresentsDefaultValue(false);
+        int index = list.getSelectionIndex();
+        int target = up ? index - 1 : index + 1;
+
+        if (index >= 0) {
+            String[] selection = list.getSelection();
+            Assert.isTrue(selection.length is 1);
+            list.remove(index);
+            list.add(selection[0], target);
+            list.setSelection(target);
+        }
+        selectionChanged();
+    }
+
+    /**
+     * Notifies that the Up button has been pressed.
+     */
+    private void upPressed() {
+        swap(true);
+    }
+
+    /*
+     * @see FieldEditor.setEnabled(bool,Composite).
+     */
+    public void setEnabled(bool enabled, Composite parent) {
+        super.setEnabled(enabled, parent);
+        getListControl(parent).setEnabled(enabled);
+        addButton.setEnabled(enabled);
+        removeButton.setEnabled(enabled);
+        upButton.setEnabled(enabled);
+        downButton.setEnabled(enabled);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/PathEditor.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.preference.PathEditor;
+
+import dwtx.jface.preference.ListEditor;
+
+import tango.io.FilePath;
+import tango.io.FileConst;
+// import java.util.ArrayList;
+// import java.util.StringTokenizer;
+
+import dwt.widgets.Composite;
+import dwt.widgets.DirectoryDialog;
+
+import dwt.dwthelper.utils;
+import tango.text.Util;
+
+/**
+ * A field editor to edit directory paths.
+ */
+public class PathEditor : ListEditor {
+
+    /**
+     * The last path, or <code>null</code> if none.
+     */
+    private String lastPath;
+
+    /**
+     * The special label text for directory chooser,
+     * or <code>null</code> if none.
+     */
+    private String dirChooserLabelText;
+
+    /**
+     * Creates a new path field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a path field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param dirChooserLabelText the label text displayed for the directory chooser
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText,
+            String dirChooserLabelText, Composite parent) {
+        init(name, labelText);
+        this.dirChooserLabelText = dirChooserLabelText;
+        createControl(parent);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on ListEditor.
+     * Creates a single string from the given array by separating each
+     * string with the appropriate OS-specific path separator.
+     */
+    protected String createList(String[] items) {
+        StringBuffer path = new StringBuffer("");//$NON-NLS-1$
+
+        for (int i = 0; i < items.length; i++) {
+            path.append(items[i]);
+            path.append(FileConst.PathSeparatorString);
+        }
+        return path.toString();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on ListEditor.
+     * Creates a new path element by means of a directory dialog.
+     */
+    protected String getNewInputObject() {
+
+        DirectoryDialog dialog = new DirectoryDialog(getShell());
+        if (dirChooserLabelText !is null) {
+            dialog.setMessage(dirChooserLabelText);
+        }
+        if (lastPath !is null) {
+            if ((new FilePath(lastPath)).exists()) {
+                dialog.setFilterPath(lastPath);
+            }
+        }
+        String dir = dialog.open();
+        if (dir !is null) {
+            dir = dwt.dwthelper.utils.trim(dir);
+            if (dir.length is 0) {
+                return null;
+            }
+            lastPath = dir;
+        }
+        return dir;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on ListEditor.
+     */
+    protected String[] parseString(String stringList) {
+        return tango.text.Util.delimit(stringList.dup, FileConst.PathSeparatorString
+                ~ "\n\r");//$NON-NLS-1$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/PreferenceContentProvider.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,118 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.preference.PreferenceContentProvider;
+
+import dwtx.jface.preference.IPreferenceNode;
+import dwtx.jface.preference.PreferenceManager;
+
+import dwtx.jface.viewers.ITreeContentProvider;
+import dwtx.jface.viewers.Viewer;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Provides a tree model for <code>PreferenceManager</code> content.
+ *
+ * @since 3.0
+ */
+public class PreferenceContentProvider : ITreeContentProvider {
+
+    private PreferenceManager manager;
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.IContentProvider#dispose()
+     */
+    public void dispose() {
+        manager = null;
+    }
+
+    /**
+     * Find the parent of the provided node.  Will search recursivly through the
+     * preference tree.
+     *
+     * @param parent the possible parent node.
+     * @param target the target child node.
+     * @return the parent node of the child node.
+     */
+    private IPreferenceNode findParent(IPreferenceNode parent,
+            IPreferenceNode target) {
+        if (parent.getId().equals(target.getId())) {
+            return null;
+        }
+
+        IPreferenceNode found = parent.findSubNode(target.getId());
+        if (found !is null) {
+            return parent;
+        }
+
+        IPreferenceNode[] children = parent.getSubNodes();
+
+        for (int i = 0; i < children.length; i++) {
+            found = findParent(children[i], target);
+            if (found !is null) {
+                return found;
+            }
+        }
+
+        return null;
+    }
+
+    /*
+     *  (non-Javadoc)
+     * @see dwtx.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
+     */
+    public Object[] getChildren(Object parentElement) {//must be an instance of <code>IPreferenceNode</code>.
+        return arraycast!(Object)((cast(IPreferenceNode) parentElement).getSubNodes());
+    }
+
+    /*
+     *  (non-Javadoc)
+     * @see dwtx.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
+     */
+    public Object[] getElements(Object inputElement) {// must be an instance of <code>PreferenceManager</code>.
+        return getChildren(cast(Object)(cast(PreferenceManager) inputElement).getRoot_package());
+    }
+
+    /*
+     *  (non-Javadoc)
+     * @see dwtx.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
+     */
+    public Object getParent(Object element) {//must be an instance of <code>IPreferenceNode</code>.
+        IPreferenceNode targetNode = cast(IPreferenceNode) element;
+        IPreferenceNode root = manager.getRoot_package();
+        return cast(Object)findParent(root, targetNode);
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
+     */
+    public bool hasChildren(Object element) {
+        return getChildren(element).length > 0;
+    }
+
+    /* (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) {
+        manager = cast(PreferenceManager) newInput;
+    }
+    /**
+     * Set the manager for the preferences.
+     * @param manager The manager to set.
+     *
+     * @since 3.1
+     */
+    protected void setManager(PreferenceManager manager) {
+        this.manager = manager;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/PreferenceConverter.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,563 @@
+/*******************************************************************************
+ * 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.preference.PreferenceConverter;
+
+import dwtx.jface.preference.IPreferenceStore;
+
+// import java.util.Arrays;
+// import java.util.StringTokenizer;
+
+import dwt.DWTException;
+import dwt.graphics.FontData;
+import dwt.graphics.Point;
+import dwt.graphics.RGB;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Display;
+import dwtx.jface.resource.JFaceResources;
+import dwtx.jface.resource.StringConverter;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A utility class for dealing with preferences whose values are
+ * common DWT objects (color, points, rectangles, and font data).
+ * The static methods on this class handle the conversion between
+ * the DWT objects and their string representations.
+ * <p>
+ * Usage:
+ * <pre>
+ * IPreferenceStore store = ...;
+ * PreferenceConverter.setValue(store, "bg", new RGB(127,127,127));
+ * ...
+ * RBG bgColor = PreferenceConverter.getValue(store, "bg");
+ * </pre>
+ * </p>
+ * <p>
+ * This class contains static methods and fields only and cannot
+ * be instantiated.
+ * </p>
+ * Note: touching this class has the side effect of creating a display (static initializer).
+ */
+public class PreferenceConverter {
+
+    /**
+     * The default-default value for point preferences
+     * (the origin, <code>(0,0)</code>).
+     */
+    public static const Point POINT_DEFAULT_DEFAULT;
+
+    /**
+     * The default-default value for rectangle preferences
+     * (the empty rectangle <code>(0,0,0,0)</code>).
+     */
+    public static const Rectangle RECTANGLE_DEFAULT_DEFAULT;
+
+    /**
+     * The default-default value for color preferences
+     * (black, <code>RGB(0,0,0)</code>).
+     */
+    public static const RGB COLOR_DEFAULT_DEFAULT;
+
+    private static const String ENTRY_SEPARATOR = ";"; //$NON-NLS-1$
+
+    /**
+     * The default-default value for <code>FontData[]</code> preferences.
+     */
+    public static const FontData[] FONTDATA_ARRAY_DEFAULT_DEFAULT;
+
+    /**
+     * The default-default value for <code>FontData</code> preferences.
+     */
+    public static const FontData FONTDATA_DEFAULT_DEFAULT;
+    static this() {
+        POINT_DEFAULT_DEFAULT = new Point(0, 0);
+        RECTANGLE_DEFAULT_DEFAULT = new Rectangle(0,
+            0, 0, 0);
+        COLOR_DEFAULT_DEFAULT = new RGB(0, 0, 0);
+        Display display = Display.getCurrent();
+        if (display is null) {
+            display = Display.getDefault ();
+        }
+
+        FONTDATA_ARRAY_DEFAULT_DEFAULT = display.getSystemFont().getFontData();
+        /**
+         * The default-default value for <code>FontData</code> preferences.
+         * This is left in for compatibility purposes. It is recommended that
+         * FONTDATA_ARRAY_DEFAULT_DEFAULT is actually used.
+         */
+
+        FONTDATA_DEFAULT_DEFAULT = FONTDATA_ARRAY_DEFAULT_DEFAULT[0];
+    }
+
+    /* (non-Javadoc)
+     * private constructor to prevent instantiation.
+     */
+    private this() {
+        //no-op
+    }
+
+    /**
+     * Helper method to construct a color from the given string.
+     * @param value the indentifier for the color
+     * @return RGB
+     */
+    private static RGB basicGetColor(String value) {
+
+        if (IPreferenceStore.STRING_DEFAULT_DEFAULT.equals(value)) {
+            return COLOR_DEFAULT_DEFAULT;
+        }
+
+        RGB color = StringConverter.asRGB(value, null);
+        if (color is null) {
+            return COLOR_DEFAULT_DEFAULT;
+        }
+        return color;
+    }
+
+    /**
+     * Helper method to construct a <code>FontData</code> from the given string.
+     * String is in the form FontData;FontData; in order that
+     * multiple FontDatas can be defined.
+     * @param value the identifier for the font
+     * @return FontData[]
+     *
+     * @since 3.0
+     */
+    public static FontData[] basicGetFontData(String value) {
+        if (IPreferenceStore.STRING_DEFAULT_DEFAULT.equals(value)) {
+            return FONTDATA_ARRAY_DEFAULT_DEFAULT;
+        }
+
+        //Read in all of them to get the value
+        auto tokens = tango.text.Util.delimit( value, ENTRY_SEPARATOR);
+        int numTokens = tokens.length;
+        FontData[] fontData = new FontData[numTokens];
+
+        for (int i = 0; i < numTokens; i++) {
+            try {
+                fontData[i] = new FontData(tokens[i]);
+            } catch (DWTException error) {
+                return FONTDATA_ARRAY_DEFAULT_DEFAULT;
+            } catch (IllegalArgumentException error) {
+                return FONTDATA_ARRAY_DEFAULT_DEFAULT;
+            }
+        }
+        return fontData;
+    }
+
+    /**
+     * Reads the supplied string and returns its corresponding
+     * FontData. If it cannot be read then the default FontData
+     * will be returned.
+     *
+     * @param fontDataValue the string value for the font data
+     * @return the font data
+     */
+    public static FontData[] readFontData(String fontDataValue) {
+        return basicGetFontData(fontDataValue);
+    }
+
+    /**
+     * Helper method to construct a point from the given string.
+     * @param value
+     * @return Point
+     */
+    private static Point basicGetPoint(String value) {
+        Point dp = new Point(POINT_DEFAULT_DEFAULT.x, POINT_DEFAULT_DEFAULT.y);
+        if (IPreferenceStore.STRING_DEFAULT_DEFAULT.equals(value)) {
+            return dp;
+        }
+        return StringConverter.asPoint(value, dp);
+    }
+
+    /**
+     *  Helper method to construct a rectangle from the given string.
+     * @param value
+     * @return Rectangle
+     */
+    private static Rectangle basicGetRectangle(String value) {
+        // We can't just return RECTANGLE_DEFAULT_DEFAULT because
+        // a rectangle object doesn't have value semantik.
+        Rectangle dr = new Rectangle(RECTANGLE_DEFAULT_DEFAULT.x,
+                RECTANGLE_DEFAULT_DEFAULT.y, RECTANGLE_DEFAULT_DEFAULT.width,
+                RECTANGLE_DEFAULT_DEFAULT.height);
+
+        if (IPreferenceStore.STRING_DEFAULT_DEFAULT.equals(value)) {
+            return dr;
+        }
+        return StringConverter.asRectangle(value, dr);
+    }
+
+    /**
+     * Returns the current value of the color-valued preference with the
+     * given name in the given preference store.
+     * Returns the default-default value (<code>COLOR_DEFAULT_DEFAULT</code>)
+     * if there is no preference with the given name, or if the current value
+     * cannot be treated as a color.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the color-valued preference
+     */
+    public static RGB getColor(IPreferenceStore store, String name) {
+        return basicGetColor(store.getString(name));
+    }
+
+    /**
+     * Returns the default value for the color-valued preference
+     * with the given name in the given preference store.
+     * Returns the default-default value (<code>COLOR_DEFAULT_DEFAULT</code>)
+     * is no default preference with the given name, or if the default
+     * value cannot be treated as a color.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the default value of the preference
+     */
+    public static RGB getDefaultColor(IPreferenceStore store, String name) {
+        return basicGetColor(store.getDefaultString(name));
+    }
+
+    /**
+     * Returns the default value array for the font-valued preference
+     * with the given name in the given preference store.
+     * Returns the default-default value (<code>FONTDATA_ARRAY_DEFAULT_DEFAULT</code>)
+     * is no default preference with the given name, or if the default
+     * value cannot be treated as font data.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the default value of the preference
+     */
+    public static FontData[] getDefaultFontDataArray(IPreferenceStore store,
+            String name) {
+        return basicGetFontData(store.getDefaultString(name));
+    }
+
+    /**
+     * Returns a single default value for the font-valued preference
+     * with the given name in the given preference store.
+     * Returns the default-default value (<code>FONTDATA_DEFAULT_DEFAULT</code>)
+     * is no default preference with the given name, or if the default
+     * value cannot be treated as font data.
+     * This method is provided for backwards compatibility. It is
+     * recommended that <code>getDefaultFontDataArray</code> is
+     * used instead.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the default value of the preference
+     */
+    public static FontData getDefaultFontData(IPreferenceStore store,
+            String name) {
+        return getDefaultFontDataArray(store, name)[0];
+    }
+
+    /**
+     * Returns the default value for the point-valued preference
+     * with the given name in the given preference store.
+     * Returns the default-default value (<code>POINT_DEFAULT_DEFAULT</code>)
+     * is no default preference with the given name, or if the default
+     * value cannot be treated as a point.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the default value of the preference
+     */
+    public static Point getDefaultPoint(IPreferenceStore store, String name) {
+        return basicGetPoint(store.getDefaultString(name));
+    }
+
+    /**
+     * Returns the default value for the rectangle-valued preference
+     * with the given name in the given preference store.
+     * Returns the default-default value (<code>RECTANGLE_DEFAULT_DEFAULT</code>)
+     * is no default preference with the given name, or if the default
+     * value cannot be treated as a rectangle.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the default value of the preference
+     */
+    public static Rectangle getDefaultRectangle(IPreferenceStore store,
+            String name) {
+        return basicGetRectangle(store.getDefaultString(name));
+    }
+
+    /**
+     * Returns the current value of the font-valued preference with the
+     * given name in the given preference store.
+     * Returns the default-default value (<code>FONTDATA_ARRAY_DEFAULT_DEFAULT</code>)
+     * if there is no preference with the given name, or if the current value
+     * cannot be treated as font data.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the font-valued preference
+     */
+    public static FontData[] getFontDataArray(IPreferenceStore store,
+            String name) {
+        return basicGetFontData(store.getString(name));
+    }
+
+    /**
+     * Returns the current value of the first entry of the
+     * font-valued preference with the
+     * given name in the given preference store.
+     * Returns the default-default value (<code>FONTDATA_ARRAY_DEFAULT_DEFAULT</code>)
+     * if there is no preference with the given name, or if the current value
+     * cannot be treated as font data.
+     * This API is provided for backwards compatibility. It is
+     * recommended that <code>getFontDataArray</code> is used instead.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the font-valued preference
+     */
+    public static FontData getFontData(IPreferenceStore store, String name) {
+        return getFontDataArray(store, name)[0];
+    }
+
+    /**
+     * Returns the current value of the point-valued preference with the
+     * given name in the given preference store.
+     * Returns the default-default value (<code>POINT_DEFAULT_DEFAULT</code>)
+     * if there is no preference with the given name, or if the current value
+     * cannot be treated as a point.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the point-valued preference
+     */
+    public static Point getPoint(IPreferenceStore store, String name) {
+        return basicGetPoint(store.getString(name));
+    }
+
+    /**
+     * Returns the current value of the rectangle-valued preference with the
+     * given name in the given preference store.
+     * Returns the default-default value (<code>RECTANGLE_DEFAULT_DEFAULT</code>)
+     * if there is no preference with the given name, or if the current value
+     * cannot be treated as a rectangle.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @return the rectangle-valued preference
+     */
+    public static Rectangle getRectangle(IPreferenceStore store, String name) {
+        return basicGetRectangle(store.getString(name));
+    }
+
+    /**
+     * Sets the default value of the preference with the given name
+     * in the given preference store. As FontDatas are stored as
+     * arrays this method is only provided for backwards compatibility.
+     * Use <code>setDefault(IPreferenceStore, String, FontData[])</code>
+     * instead.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new default value of the preference
+     */
+    public static void setDefault(IPreferenceStore store, String name,
+            FontData value) {
+        FontData[] fontDatas = new FontData[1];
+        fontDatas[0] = value;
+        setDefault(store, name, fontDatas);
+    }
+
+    /**
+     * Sets the default value of the preference with the given name
+     * in the given preference store.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new default value of the preference
+     */
+    public static void setDefault(IPreferenceStore store, String name,
+            FontData[] value) {
+        store.setDefault(name, getStoredRepresentation(value));
+    }
+
+    /**
+     * Sets the default value of the preference with the given name
+     * in the given preference store.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new default value of the preference
+     */
+    public static void setDefault(IPreferenceStore store, String name,
+            Point value) {
+        store.setDefault(name, StringConverter.asString(value));
+    }
+
+    /**
+     * Sets the default value of the preference with the given name
+     * in the given preference store.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new default value of the preference
+     */
+    public static void setDefault(IPreferenceStore store, String name,
+            Rectangle value) {
+        store.setDefault(name, StringConverter.asString(value));
+    }
+
+    /**
+     * Sets the default value of the preference with the given name
+     * in the given preference store.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new default value of the preference
+     */
+    public static void setDefault(IPreferenceStore store, String name, RGB value) {
+        store.setDefault(name, StringConverter.asString(value));
+    }
+
+    /**
+     * Sets the current value of the preference with the given name
+     * in the given preference store.
+     * <p>
+     * Included for backwards compatibility.  This method is equivalent to
+     * </code>setValue(store, name, new FontData[]{value})</code>.
+     * </p>
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     */
+    public static void setValue(IPreferenceStore store, String name,
+            FontData value) {
+        setValue(store, name, [ value ]);
+    }
+
+    /**
+     * Sets the current value of the preference with the given name
+     * in the given preference store. This method also sets the corresponding
+     * key in the JFace font registry to the value and fires a
+     * property change event to listeners on the preference store.
+     *
+     * <p>
+     * Note that this API does not update any other settings that may
+     * be dependant upon it. Only the value in the preference store
+     * and in the font registry is updated.
+     * </p>
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     *
+     * @see #putValue(IPreferenceStore, String, FontData[])
+     */
+    public static void setValue(IPreferenceStore store, String name,
+            FontData[] value) {
+        FontData[] oldValue = getFontDataArray(store, name);
+        // see if the font has changed
+        if (!Arrays.equals(oldValue, value)) {
+            store.putValue(name, getStoredRepresentation(value));
+            JFaceResources.getFontRegistry().put(name, value);
+            store.firePropertyChangeEvent(name, new ArrayWrapperT!(FontData)(oldValue), new ArrayWrapperT!(FontData)(value));
+        }
+    }
+
+    /**
+     * Sets the current value of the preference with the given name
+     * in the given preference store. This method does not update
+     * the font registry or fire a property change event.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     *
+     * @see PreferenceConverter#setValue(IPreferenceStore, String, FontData[])
+     */
+    public static void putValue(IPreferenceStore store, String name,
+            FontData[] value) {
+        FontData[] oldValue = getFontDataArray(store, name);
+        // see if the font has changed
+        if (!Arrays.equals(oldValue, value)) {
+            store.putValue(name, getStoredRepresentation(value));
+        }
+    }
+
+    /**
+     * Returns the stored representation of the given array of FontData objects.
+     * The stored representation has the form FontData;FontData;
+     * Only includes the non-null entries.
+     *
+     * @param fontData the array of FontData objects
+     * @return the stored representation of the FontData objects
+     * @since 3.0
+     */
+    public static String getStoredRepresentation(FontData[] fontData) {
+        StringBuffer buffer = new StringBuffer();
+        for (int i = 0; i < fontData.length; i++) {
+            if (fontData[i] !is null) {
+                buffer.append(fontData[i].toString());
+                buffer.append(ENTRY_SEPARATOR);
+            }
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Sets the current value of the preference with the given name
+     * in the given preference store.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     */
+    public static void setValue(IPreferenceStore store, String name, Point value) {
+        Point oldValue = getPoint(store, name);
+        if (oldValue is null || !oldValue.opEquals(value)) {
+            store.putValue(name, StringConverter.asString(value));
+            store.firePropertyChangeEvent(name, oldValue, value);
+        }
+    }
+
+    /**
+     * Sets the current value of the preference with the given name
+     * in the given preference store.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     */
+    public static void setValue(IPreferenceStore store, String name,
+            Rectangle value) {
+        Rectangle oldValue = getRectangle(store, name);
+        if (oldValue is null || !oldValue.opEquals(value)) {
+            store.putValue(name, StringConverter.asString(value));
+            store.firePropertyChangeEvent(name, oldValue, value);
+        }
+    }
+
+    /**
+     * Sets the current value of the preference with the given name
+     * in the given preference store.
+     *
+     * @param store the preference store
+     * @param name the name of the preference
+     * @param value the new current value of the preference
+     */
+    public static void setValue(IPreferenceStore store, String name, RGB value) {
+        RGB oldValue = getColor(store, name);
+        if (oldValue is null || !oldValue.opEquals(value)) {
+            store.putValue(name, StringConverter.asString(value));
+            store.firePropertyChangeEvent(name, oldValue, value);
+        }
+    }
+}
--- a/dwtx/jface/preference/PreferenceDialog.d	Thu Apr 03 20:41:52 2008 +0200
+++ b/dwtx/jface/preference/PreferenceDialog.d	Sat Apr 05 01:45:47 2008 +0200
@@ -14,17 +14,15 @@
  *******************************************************************************/
 module dwtx.jface.preference.PreferenceDialog;
 
-import dwt.dwthelper.utils;
-
-pragma( msg, "FIXME dwtx.jface.preference.PreferenceDialog" );
-class PreferenceDialog{
-    public static const String PREF_DLG_TITLE_IMG = "preference_dialog_title_image"; //$NON-NLS-1$
-}
-
-/++
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.List;
+import dwtx.jface.preference.IPreferencePageContainer;
+import dwtx.jface.preference.IPreferencePage;
+import dwtx.jface.preference.IPreferenceNode;
+import dwtx.jface.preference.IPreferenceStore;
+import dwtx.jface.preference.IPersistentPreferenceStore;
+import dwtx.jface.preference.PreferenceManager;
+import dwtx.jface.preference.PreferencePage;
+import dwtx.jface.preference.PreferenceLabelProvider;
+import dwtx.jface.preference.PreferenceContentProvider;
 
 import dwt.DWT;
 import dwt.custom.BusyIndicator;
@@ -84,18 +82,21 @@
 import dwtx.jface.viewers.TreeViewer;
 import dwtx.jface.viewers.ViewerFilter;
 
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Runnable;
+
 /**
  * A preference dialog is a hierarchical presentation of preference pages. Each
  * page is represented by a node in the tree shown on the left hand side of the
  * dialog; when a node is selected, the corresponding page is shown on the right
  * hand side.
  */
-public class PreferenceDialog extends TrayDialog implements IPreferencePageContainer, IPageChangeProvider {
+public class PreferenceDialog : TrayDialog, IPreferencePageContainer, IPageChangeProvider {
     /**
      * Layout for the page container.
      *
      */
-    private class PageLayout extends Layout {
+    private class PageLayout : Layout {
         public Point computeSize(Composite composite, int wHint, int hHint, bool force) {
             if (wHint !is DWT.DEFAULT && hHint !is DWT.DEFAULT) {
                 return new Point(wHint, hHint);
@@ -144,17 +145,17 @@
     /**
      * Indentifier for the error image
      */
-    public static final String PREF_DLG_IMG_TITLE_ERROR = DLG_IMG_MESSAGE_ERROR;
+    public static const String PREF_DLG_IMG_TITLE_ERROR = DLG_IMG_MESSAGE_ERROR;
 
     /**
      * Title area fields
      */
-    public static final String PREF_DLG_TITLE_IMG = "preference_dialog_title_image"; //$NON-NLS-1$
+    public static const String PREF_DLG_TITLE_IMG = "preference_dialog_title_image"; //$NON-NLS-1$
 
     /**
      * Return code used when dialog failed
      */
-    protected static final int FAILED = 2;
+    protected static const int FAILED = 2;
 
     /**
      * The current preference page, or <code>null</code> if there is none.
@@ -172,7 +173,7 @@
      *
      * @see #setMinimumPageSize(Point)
      */
-    private Point minimumPageSize = new Point(400, 400);
+    private Point minimumPageSize;
 
     /**
      * The OK button.
@@ -208,7 +209,7 @@
      */
     private TreeViewer treeViewer;
 
-    private ListenerList pageChangedListeners = new ListenerList();
+    private ListenerList pageChangedListeners;
 
     /**
      *  Composite with a FormLayout to contain the title area
@@ -226,7 +227,9 @@
      * @param manager
      *            the preference manager
      */
-    public PreferenceDialog(Shell parentShell, PreferenceManager manager) {
+    public this(Shell parentShell, PreferenceManager manager) {
+        minimumPageSize = new Point(400, 400);
+        pageChangedListeners = new ListenerList();
         super(parentShell);
         setShellStyle(getShellStyle() | DWT.RESIZE | DWT.MAX);
         preferenceManager = manager;
@@ -261,13 +264,21 @@
      */
     protected void cancelPressed() {
         // Inform all pages that we are cancelling
-        Iterator nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER).iterator();
-        while (nodes.hasNext()) {
-            final IPreferenceNode node = (IPreferenceNode) nodes.next();
+        foreach( e; preferenceManager.getElements(PreferenceManager.PRE_ORDER)){
+            IPreferenceNode node = cast(IPreferenceNode) e;
             if (getPage(node) !is null) {
-                SafeRunnable.run(new SafeRunnable() {
+
+                // this_: strange workaround for compiler error with dmd 1.028 in run()
+                SafeRunnable.run(new class(this) SafeRunnable {
+                    PreferenceDialog this_;
+                    IPreferenceNode node_;
+                    this(PreferenceDialog outer_){
+                        node_=node;
+                        this_=outer_;
+                        auto p = this_.getPage( node );
+                    }
                     public void run() {
-                        if (!getPage(node).performCancel()) {
+                        if (!this_.getPage(node_).performCancel()) {
                             return;
                         }
                     }
@@ -294,14 +305,14 @@
     public bool close() {
 
         //Do this is in a SafeRunnable as it may run client code
-        SafeRunnable runnable = new SafeRunnable(){
+        SafeRunnable runnable = new class SafeRunnable{
             /* (non-Javadoc)
              * @see dwtx.core.runtime.ISafeRunnable#run()
              */
-            public void run() throws Exception {
-                List nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER);
+            public void run() {
+                auto nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER);
                 for (int i = 0; i < nodes.size(); i++) {
-                    IPreferenceNode node = (IPreferenceNode) nodes.get(i);
+                    IPreferenceNode node = cast(IPreferenceNode) nodes.get(i);
                     node.disposeResources();
                 }
 
@@ -310,7 +321,7 @@
             /* (non-Javadoc)
              * @see dwtx.jface.util.SafeRunnable#handleException(java.lang.Throwable)
              */
-            public void handleException(Throwable e) {
+            public void handleException(Exception e) {
                 super.handleException(e);
                 clearSelectedNode();//Do not cache a node with problems
             }
@@ -329,7 +340,7 @@
     protected void configureShell(Shell newShell) {
         super.configureShell(newShell);
         newShell.setText(JFaceResources.getString("PreferenceDialog.title")); //$NON-NLS-1$
-        newShell.addShellListener(new ShellAdapter() {
+        newShell.addShellListener(new class ShellAdapter {
             public void shellActivated(ShellEvent e) {
                 if (lastShellSize is null) {
                     lastShellSize = getShell().getSize();
@@ -370,11 +381,15 @@
      *
      * @see dwtx.jface.window.Window#createContents(dwt.widgets.Composite)
      */
-    protected Control createContents(final Composite parent) {
-        final Control[] control = new Control[1];
-        BusyIndicator.showWhile(getShell().getDisplay(), new Runnable() {
+    protected Control createContents(Composite parent) {
+        Control[1] control;
+        BusyIndicator.showWhile(getShell().getDisplay(), new class Runnable {
+            Composite parent_;
+            this(){
+                parent_=parent;
+            }
             public void run() {
-                control[0] = PreferenceDialog.super.createContents(parent);
+                control[0] = callSuperCreateContents(parent);
                 // Add the first page
                 selectSavedItem();
             }
@@ -382,15 +397,17 @@
 
         return control[0];
     }
-
+    private Control callSuperCreateContents( Composite c ){
+        return super.createContents( c );
+    }
     /*
      * (non-Javadoc)
      *
      * @see dwtx.jface.dialogs.Dialog#createDialogArea(dwt.widgets.Composite)
      */
     protected Control createDialogArea(Composite parent) {
-        final Composite composite = (Composite) super.createDialogArea(parent);
-        GridLayout parentLayout = ((GridLayout) composite.getLayout());
+        final Composite composite = cast(Composite) super.createDialogArea(parent);
+        GridLayout parentLayout = (cast(GridLayout) composite.getLayout());
         parentLayout.numColumns = 4;
         parentLayout.marginHeight = 0;
         parentLayout.marginWidth = 0;
@@ -470,13 +487,21 @@
      *
      * @since 3.1
      */
-    protected Sash createSash(final Composite composite, final Control rightControl) {
-        final Sash sash = new Sash(composite, DWT.VERTICAL);
+    protected Sash createSash(Composite composite, Control rightControl) {
+        Sash sash = new Sash(composite, DWT.VERTICAL);
         sash.setLayoutData(new GridData(GridData.FILL_VERTICAL));
         sash.setBackground(composite.getDisplay().getSystemColor(DWT.COLOR_LIST_BACKGROUND));
         // the following listener resizes the tree control based on sash deltas.
         // If necessary, it will also grow/shrink the dialog.
-        sash.addListener(DWT.Selection, new Listener() {
+        sash.addListener(DWT.Selection, new class Listener {
+            Composite composite_;
+            Control rightControl_;
+            Sash sash_;
+            this(){
+                composite_=composite;
+                rightControl_=rightControl;
+                sash_=sash;
+            }
             /*
              * (non-Javadoc)
              *
@@ -486,8 +511,8 @@
                 if (event.detail is DWT.DRAG) {
                     return;
                 }
-                int shift = event.x - sash.getBounds().x;
-                GridData data = (GridData) rightControl.getLayoutData();
+                int shift = event.x - sash_.getBounds().x;
+                GridData data = cast(GridData) rightControl_.getLayoutData();
                 int newWidthHint = data.widthHint + shift;
                 if (newWidthHint < 20) {
                     return;
@@ -496,10 +521,10 @@
                 Point currentSize = getShell().getSize();
                 // if the dialog wasn't of a custom size we know we can shrink
                 // it if necessary based on sash movement.
-                bool customSize = !computedSize.equals(currentSize);
+                bool customSize = !computedSize.opEquals(currentSize);
                 data.widthHint = newWidthHint;
                 setLastTreeWidth(newWidthHint);
-                composite.layout(true);
+                composite_.layout(true);
                 // recompute based on new widget size
                 computedSize = getShell().computeSize(DWT.DEFAULT, DWT.DEFAULT);
                 // if the dialog was of a custom size then increase it only if
@@ -508,7 +533,7 @@
                     computedSize.x = Math.max(computedSize.x, currentSize.x);
                 }
                 computedSize.y = Math.max(computedSize.y, currentSize.y);
-                if (computedSize.equals(currentSize)) {
+                if (computedSize.opEquals(currentSize)) {
                     return;
                 }
                 setShellSize(computedSize.x, computedSize.y);
@@ -596,7 +621,7 @@
         messageArea = new DialogMessageArea();
         messageArea.createContents(titleArea);
 
-        titleArea.addControlListener(new ControlAdapter() {
+        titleArea.addControlListener(new class ControlAdapter {
             /* (non-Javadoc)
              * @see dwt.events.ControlAdapter#controlResized(dwt.events.ControlEvent)
              */
@@ -605,7 +630,7 @@
             }
         });
 
-        final IPropertyChangeListener fontListener = new IPropertyChangeListener() {
+        final IPropertyChangeListener fontListener = new class IPropertyChangeListener {
             public void propertyChange(PropertyChangeEvent event) {
                 if (JFaceResources.BANNER_FONT.equals(event.getProperty())) {
                     updateMessage();
@@ -614,7 +639,7 @@
                     updateMessage();
                     Font dialogFont = JFaceResources.getDialogFont();
                     updateTreeFont(dialogFont);
-                    Control[] children = ((Composite) buttonBar).getChildren();
+                    Control[] children = (cast(Composite) buttonBar).getChildren();
                     for (int i = 0; i < children.length; i++) {
                         children[i].setFont(dialogFont);
                     }
@@ -622,7 +647,7 @@
             }
         };
 
-        titleArea.addDisposeListener(new DisposeListener() {
+        titleArea.addDisposeListener(new class DisposeListener {
             public void widgetDisposed(DisposeEvent event) {
                 JFaceResources.getFontRegistry().removeListener(fontListener);
             }
@@ -684,50 +709,58 @@
      *
      * @since 3.1
      */
-    protected void addListeners(final TreeViewer viewer) {
-        viewer.addPostSelectionChangedListener(new ISelectionChangedListener() {
+    protected void addListeners(TreeViewer viewer) {
+        viewer.addPostSelectionChangedListener(new class ISelectionChangedListener {
+            TreeViewer viewer_;
+            this(){
+                viewer_=viewer;
+            }
             private void handleError() {
                 try {
                     // remove the listener temporarily so that the events caused
                     // by the error handling dont further cause error handling
                     // to occur.
-                    viewer.removePostSelectionChangedListener(this);
+                    viewer_.removePostSelectionChangedListener(this);
                     showPageFlippingAbortDialog();
                     selectCurrentPageAgain();
                     clearSelectedNode();
                 } finally {
-                    viewer.addPostSelectionChangedListener(this);
+                    viewer_.addPostSelectionChangedListener(this);
                 }
             }
 
             public void selectionChanged(SelectionChangedEvent event) {
-                Object selection = getSingleSelection(event.getSelection());
-                if (selection instanceof IPreferenceNode) {
+                Object selection = cast(Object) getSingleSelection(event.getSelection());
+                if (cast(IPreferenceNode)selection ) {
                     if (!isCurrentPageValid()) {
                         handleError();
-                    } else if (!showPage((IPreferenceNode) selection)) {
+                    } else if (!showPage(cast(IPreferenceNode) selection)) {
                         // Page flipping wasn't successful
                         handleError();
                     } else {
                         // Everything went well
-                        lastSuccessfulNode = (IPreferenceNode) selection;
+                        lastSuccessfulNode = cast(IPreferenceNode) selection;
                     }
                 }
             }
         });
-        ((Tree) viewer.getControl()).addSelectionListener(new SelectionAdapter() {
-            public void widgetDefaultSelected(final SelectionEvent event) {
-                ISelection selection = viewer.getSelection();
+        (cast(Tree) viewer.getControl()).addSelectionListener(new class SelectionAdapter {
+            TreeViewer viewer_;
+            this(){
+                viewer_=viewer;
+            }
+            public void widgetDefaultSelected(SelectionEvent event) {
+                ISelection selection = viewer_.getSelection();
                 if (selection.isEmpty()) {
                     return;
                 }
                 IPreferenceNode singleSelection = getSingleSelection(selection);
-                bool expanded = viewer.getExpandedState(singleSelection);
-                viewer.setExpandedState(singleSelection, !expanded);
+                bool expanded = viewer_.getExpandedState(cast(Object)singleSelection);
+                viewer_.setExpandedState(cast(Object)singleSelection, !expanded);
             }
         });
         //Register help listener on the tree to use context sensitive help
-        viewer.getControl().addHelpListener(new HelpListener() {
+        viewer.getControl().addHelpListener(new class HelpListener {
             public void helpRequested(HelpEvent event) {
                 // call perform help on the current page
                 if (currentPage !is null) {
@@ -747,9 +780,8 @@
      *         found.
      */
     protected IPreferenceNode findNodeMatching(String nodeId) {
-        List nodes = preferenceManager.getElements(PreferenceManager.POST_ORDER);
-        for (Iterator i = nodes.iterator(); i.hasNext();) {
-            IPreferenceNode node = (IPreferenceNode) i.next();
+        foreach( e; preferenceManager.getElements(PreferenceManager.POST_ORDER)){
+            IPreferenceNode node = cast(IPreferenceNode) e;
             if (node.getId().equals(nodeId)) {
                 return node;
             }
@@ -800,9 +832,9 @@
      */
     protected IPreferenceNode getSingleSelection(ISelection selection) {
         if (!selection.isEmpty()) {
-            IStructuredSelection structured = (IStructuredSelection) selection;
-            if (structured.getFirstElement() instanceof IPreferenceNode) {
-                return (IPreferenceNode) structured.getFirstElement();
+            IStructuredSelection structured = cast(IStructuredSelection) selection;
+            if (cast(IPreferenceNode)structured.getFirstElement() ) {
+                return cast(IPreferenceNode) structured.getFirstElement();
             }
         }
         return null;
@@ -828,17 +860,16 @@
      * </p>
      */
     protected void handleSave() {
-        Iterator nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER).iterator();
-        while (nodes.hasNext()) {
-            IPreferenceNode node = (IPreferenceNode) nodes.next();
+        foreach( e; preferenceManager.getElements(PreferenceManager.PRE_ORDER)){
+            IPreferenceNode node = cast(IPreferenceNode) e;
             IPreferencePage page = node.getPage();
-            if (page instanceof PreferencePage) {
+            if (cast(PreferencePage)page ) {
                 // Save now in case tbe workbench does not shutdown cleanly
-                IPreferenceStore store = ((PreferencePage) page).getPreferenceStore();
+                IPreferenceStore store = (cast(PreferencePage) page).getPreferenceStore();
                 if (store !is null && store.needsSaving()
-                        && store instanceof IPersistentPreferenceStore) {
+                        && cast(IPersistentPreferenceStore)store ) {
                     try {
-                        ((IPersistentPreferenceStore) store).save();
+                        (cast(IPersistentPreferenceStore) store).save();
                     } catch (IOException e) {
                         MessageDialog
                                 .openError(
@@ -846,7 +877,7 @@
                                         JFaceResources.getString("PreferenceDialog.saveErrorTitle"), //$NON-NLS-1$
                                         JFaceResources
                                                 .format(
-                                                        "PreferenceDialog.saveErrorMessage", new Object[] { page.getTitle(), e.getMessage() })); //$NON-NLS-1$
+                                                        "PreferenceDialog.saveErrorMessage", [ page.getTitle(), e.msg ])); //$NON-NLS-1$
                     }
                 }
             }
@@ -913,7 +944,7 @@
      * save any state, and then calls <code>close</code> to close this dialog.
      */
     protected void okPressed() {
-        SafeRunnable.run(new SafeRunnable() {
+        SafeRunnable.run(new class SafeRunnable {
             private bool errorOccurred;
 
             /*
@@ -927,10 +958,8 @@
                 bool hasFailedOK = false;
                 try {
                     // Notify all the pages and give them a chance to abort
-                    Iterator nodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER)
-                            .iterator();
-                    while (nodes.hasNext()) {
-                        IPreferenceNode node = (IPreferenceNode) nodes.next();
+                    foreach( e; preferenceManager.getElements(PreferenceManager.PRE_ORDER)){
+                        IPreferenceNode node = cast(IPreferenceNode) e;
                         IPreferencePage page = node.getPage();
                         if (page !is null) {
                             if (!page.performOk()){
@@ -946,16 +975,18 @@
                     if(hasFailedOK){
                         setReturnCode(FAILED);
                         getButton(IDialogConstants.OK_ID).setEnabled(true);
-                        return;
+                        //return;
                     }
+                    else{
 
-                    if (!errorOccurred) {
-                        //Give subclasses the choice to save the state of the
-                        //preference pages.
-                        handleSave();
+                        if (!errorOccurred) {
+                            //Give subclasses the choice to save the state of the
+                            //preference pages.
+                            handleSave();
+                        }
+                        setReturnCode(OK);
+                        close();
                     }
-                    setReturnCode(OK);
-                    close();
                 }
             }
 
@@ -964,7 +995,7 @@
              *
              * @see dwtx.core.runtime.ISafeRunnable#handleException(java.lang.Throwable)
              */
-            public void handleException(Throwable e) {
+            public void handleException(Exception e) {
                 errorOccurred = true;
 
                 Policy.getLog().log(new Status(IStatus.ERROR, Policy.JFACE, 0, e.toString(), e));
@@ -985,7 +1016,7 @@
         if (lastSuccessfulNode is null) {
             return;
         }
-        getTreeViewer().setSelection(new StructuredSelection(lastSuccessfulNode));
+        getTreeViewer().setSelection(new StructuredSelection(cast(Object)lastSuccessfulNode));
         currentPage.setVisible(true);
     }
 
@@ -1002,8 +1033,8 @@
                 IPreferenceNode selectedNode = nodes[i];
                 // See if it passes all filters
                 for (int j = 0; j < filters.length; j++) {
-                    if (!filters[j].select(this.treeViewer, preferenceManager
-                            .getRoot(), selectedNode)) {
+                    if (!filters[j].select(this.treeViewer, cast(Object)preferenceManager
+                            .getRoot_package(), cast(Object)selectedNode)) {
                         selectedNode = null;
                         break;
                     }
@@ -1016,7 +1047,7 @@
             }
         }
         if (node !is null) {
-            getTreeViewer().setSelection(new StructuredSelection(node), true);
+            getTreeViewer().setSelection(new StructuredSelection(cast(Object)node), true);
             // Keep focus in tree. See bugs 2692, 2621, and 6775.
             getTreeViewer().getControl().setFocus();
         }
@@ -1122,7 +1153,7 @@
      * @see #getPreferenceStore
      */
     public void setPreferenceStore(IPreferenceStore store) {
-        Assert.isNotNull(store);
+        Assert.isNotNull(cast(Object)store);
         preferenceStore = store;
     }
 
@@ -1131,9 +1162,9 @@
      */
     private void setSelectedNode() {
         String storeValue = null;
-        IStructuredSelection selection = (IStructuredSelection) getTreeViewer().getSelection();
+        IStructuredSelection selection = cast(IStructuredSelection) getTreeViewer().getSelection();
         if (selection.size() is 1) {
-            IPreferenceNode node = (IPreferenceNode) selection.getFirstElement();
+            IPreferenceNode node = cast(IPreferenceNode) selection.getFirstElement();
             storeValue = node.getId();
         }
         setSelectedNodePreference(storeValue);
@@ -1213,9 +1244,9 @@
         // Ensure that the page control has been created
         // (this allows lazy page control creation)
         if (currentPage.getControl() is null) {
-            final bool[] failed = { false };
-            SafeRunnable.run(new ISafeRunnable() {
-                public void handleException(Throwable e) {
+            bool[1] failed; failed[0] = false;
+            SafeRunnable.run(new class ISafeRunnable {
+                public void handleException(Exception e) {
                     failed[0] = true;
                 }
 
@@ -1235,8 +1266,8 @@
         // label can be wrapped.
         final Point[] size = new Point[1];
         final Point failed = new Point(-1, -1);
-        SafeRunnable.run(new ISafeRunnable() {
-            public void handleException(Throwable e) {
+        SafeRunnable.run(new class ISafeRunnable {
+            public void handleException(Exception e) {
                 size[0] = failed;
             }
 
@@ -1244,7 +1275,7 @@
                 size[0] = currentPage.computeSize();
             }
         });
-        if (size[0].equals(failed)) {
+        if (size[0].opEquals(failed)) {
             return false;
         }
         Point contentSize = size[0];
@@ -1259,7 +1290,7 @@
             Point containerSize = new Point(rect.width, rect.height);
             int hdiff = contentSize.x - containerSize.x;
             int vdiff = contentSize.y - containerSize.y;
-            if ((hdiff > 0 || vdiff > 0) && shellSize.equals(lastShellSize)) {
+            if ((hdiff > 0 || vdiff > 0) && shellSize.opEquals(lastShellSize)) {
                     hdiff = Math.max(0, hdiff);
                     vdiff = Math.max(0, vdiff);
                     setShellSize(shellSize.x + hdiff, shellSize.y + vdiff);
@@ -1336,7 +1367,7 @@
         updateButtons();
         //Saved the selected node in the preferences
         setSelectedNode();
-        firePageChanged(new PageChangedEvent(this, getCurrentPage()));
+        firePageChanged(new PageChangedEvent(this, cast(Object)getCurrentPage()));
     }
 
     /*
@@ -1361,8 +1392,8 @@
             errorMessage = currentPage.getErrorMessage();
         }
         int messageType = IMessageProvider.NONE;
-        if (message !is null && currentPage instanceof IMessageProvider) {
-            messageType = ((IMessageProvider) currentPage).getMessageType();
+        if (message !is null && cast(IMessageProvider)currentPage ) {
+            messageType = (cast(IMessageProvider) currentPage).getMessageType();
         }
 
         if (errorMessage is null){
@@ -1472,7 +1503,7 @@
      * @since 3.1
      */
     public Object getSelectedPage() {
-            return getCurrentPage();
+            return cast(Object)getCurrentPage();
         }
 
     /**
@@ -1480,7 +1511,7 @@
      * @since 3.1
      */
     public void addPageChangedListener(IPageChangedListener listener) {
-        pageChangedListeners.add(listener);
+        pageChangedListeners.add(cast(Object)listener);
     }
 
     /**
@@ -1488,7 +1519,7 @@
      * @since 3.1
      */
     public void removePageChangedListener(IPageChangedListener listener) {
-        pageChangedListeners.remove(listener);
+        pageChangedListeners.remove(cast(Object)listener);
 
     }
 
@@ -1503,16 +1534,20 @@
      *
      * @since 3.1
      */
-    protected void firePageChanged(final PageChangedEvent event) {
+    protected void firePageChanged(PageChangedEvent event) {
         Object[] listeners = pageChangedListeners.getListeners();
         for (int i = 0; i < listeners.length; i++) {
-            final IPageChangedListener l = (IPageChangedListener) listeners[i];
-            SafeRunnable.run(new SafeRunnable() {
+            SafeRunnable.run(new class SafeRunnable {
+                PageChangedEvent event_;
+                IPageChangedListener l;
+                this(){
+                    event_=event;
+                    l = cast(IPageChangedListener) listeners[i];
+                }
                 public void run() {
-                    l.pageChanged(event);
+                    l.pageChanged(event_);
                 }
             });
         }
     }
 }
-++/
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/PreferenceLabelProvider.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.preference.PreferenceLabelProvider;
+
+import dwtx.jface.preference.IPreferenceNode;
+
+import dwt.graphics.Image;
+import dwtx.jface.viewers.LabelProvider;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Provides labels for <code>IPreferenceNode</code> objects.
+ *
+ * @since 3.0
+ */
+public class PreferenceLabelProvider : LabelProvider {
+
+    /**
+     * @param element must be an instance of <code>IPreferenceNode</code>.
+     * @see dwtx.jface.viewers.ILabelProvider#getText(java.lang.Object)
+     */
+    public String getText(Object element) {
+        return (cast(IPreferenceNode) element).getLabelText();
+    }
+
+    /**
+     * @param element must be an instance of <code>IPreferenceNode</code>.
+     * @see dwtx.jface.viewers.ILabelProvider#getImage(java.lang.Object)
+     */
+    public Image getImage(Object element) {
+        return (cast(IPreferenceNode) element).getLabelImage();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/PreferenceManager.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,257 @@
+/*******************************************************************************
+ * 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.preference.PreferenceManager;
+
+import dwtx.jface.preference.PreferenceNode;
+import dwtx.jface.preference.IPreferenceNode;
+
+import tango.util.collection.ArraySeq;
+import tango.util.collection.model.Seq;
+// import java.util.List;
+// import java.util.StringTokenizer;
+
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+import tango.text.Util;
+
+/**
+ * A preference manager maintains a hierarchy of preference nodes and
+ * associated preference pages.
+ */
+public class PreferenceManager {
+    /**
+     * Pre-order traversal means visit the root first,
+     * then the children.
+     */
+    public static const int PRE_ORDER = 0;
+
+    /**
+     * Post-order means visit the children, and then the root.
+     */
+    public static const int POST_ORDER = 1;
+
+    /**
+     * The root node.
+     * Note that the root node is a special internal node
+     * that is used to collect together all the nodes that
+     * have no parent; it is not given out to clients.
+     */
+    PreferenceNode root;
+
+    /**
+     * The path separator character.
+     */
+    String separator;
+
+    /**
+     * Creates a new preference manager.
+     */
+    public this() {
+        this('.');
+    }
+
+    /**
+     * Creates a new preference manager with the given
+     * the path separator.
+     *
+     * @param separatorChar the separator character
+     */
+    public this(char separatorChar) {
+        root = new PreferenceNode("");//$NON-NLS-1$
+        separator = [ separatorChar ];
+    }
+
+    /**
+     * Adds the given preference node as a subnode of the
+     * node at the given path.
+     *
+     * @param path the path
+     * @param node the node to add
+     * @return <code>true</code> if the add was successful,
+     *  and <code>false</code> if there is no contribution at
+     *  the given path
+     */
+    public bool addTo(String path, IPreferenceNode node) {
+        IPreferenceNode target = find(path);
+        if (target is null) {
+            return false;
+        }
+        target.add(node);
+        return true;
+    }
+
+    /**
+     * Adds the given preference node as a subnode of the
+     * root.
+     *
+     * @param node the node to add, which must implement
+     *   <code>IPreferenceNode</code>
+     */
+    public void addToRoot(IPreferenceNode node) {
+        Assert.isNotNull(cast(Object)node);
+        root.add(node);
+    }
+
+    /**
+     * Recursively enumerates all nodes at or below the given node
+     * and adds them to the given list in the given order.
+     *
+     * @param node the starting node
+     * @param sequence a read-write list of preference nodes
+     *  (element type: <code>IPreferenceNode</code>)
+     *  in the given order
+     * @param order the traversal order, one of
+     *  <code>PRE_ORDER</code> and <code>POST_ORDER</code>
+     */
+    protected void buildSequence(IPreferenceNode node, Seq!(Object) sequence, int order) {
+        if (order is PRE_ORDER) {
+            sequence.append(cast(Object)node);
+        }
+        IPreferenceNode[] subnodes = node.getSubNodes();
+        for (int i = 0; i < subnodes.length; i++) {
+            buildSequence(subnodes[i], sequence, order);
+        }
+        if (order is POST_ORDER) {
+            sequence.append(cast(Object)node);
+        }
+    }
+
+    /**
+     * Finds and returns the contribution node at the given path.
+     *
+     * @param path the path
+     * @return the node, or <code>null</code> if none
+     */
+    public IPreferenceNode find(String path) {
+       return find(path,root);
+    }
+
+    /**
+     * Finds and returns the preference node directly
+     * below the top at the given path.
+     *
+     * @param path the path
+     * @param top top at the given path
+     * @return the node, or <code>null</code> if none
+     *
+     * @since 3.1
+     */
+    protected IPreferenceNode find(String path,IPreferenceNode top){
+         Assert.isNotNull(path);
+         auto tokens = tango.text.Util.delimit( path, separator );
+         IPreferenceNode node = top;
+         foreach( id; tokens ){
+             node = node.findSubNode(id);
+             if (node is null) {
+                return null;
+            }
+         }
+         if (node is top) {
+            return null;
+        }
+         return node;
+    }
+
+    /**
+     * Returns all preference nodes managed by this
+     * manager.
+     *
+     * @param order the traversal order, one of
+     *  <code>PRE_ORDER</code> and <code>POST_ORDER</code>
+     * @return a list of preference nodes
+     *  (element type: <code>IPreferenceNode</code>)
+     *  in the given order
+     */
+    public Seq!(Object) getElements(int order) {
+        Assert.isTrue(order is PRE_ORDER || order is POST_ORDER,
+                "invalid traversal order");//$NON-NLS-1$
+        auto sequence = new ArraySeq!(Object);
+        IPreferenceNode[] subnodes = getRoot().getSubNodes();
+        for (int i = 0; i < subnodes.length; i++) {
+            buildSequence(subnodes[i], sequence, order);
+        }
+        return sequence;
+    }
+
+    /**
+     * Returns the root node.
+     * Note that the root node is a special internal node
+     * that is used to collect together all the nodes that
+     * have no parent; it is not given out to clients.
+     *
+     * @return the root node
+     */
+    protected IPreferenceNode getRoot() {
+        return root;
+    }
+    package IPreferenceNode getRoot_package() {
+        return getRoot();
+    }
+
+    /**
+     * Returns the root level nodes of this preference manager.
+     *
+     * @return an array containing the root nodes
+     * @since 3.2
+     */
+    public final IPreferenceNode[] getRootSubNodes() {
+        return getRoot().getSubNodes();
+    }
+
+    /**
+     * Removes the preference node at the given path.
+     *
+     * @param path
+     *            the path
+     * @return the node that was removed, or <code>null</code> if there was no
+     *         node at the given path
+     */
+    public IPreferenceNode remove(String path) {
+        Assert.isNotNull(path);
+        int index = path.lastIndexOf(separator);
+        if (index is -1) {
+            return root.remove(path);
+        }
+        // Make sure that the last character in the string isn't the "."
+        Assert.isTrue(index < path.length - 1, "Path can not end with a dot");//$NON-NLS-1$
+        String parentPath = path.substring(0, index);
+        String id = path.substring(index + 1);
+        IPreferenceNode parentNode = find(parentPath);
+        if (parentNode is null) {
+            return null;
+        }
+        return parentNode.remove(id);
+    }
+
+    /**
+     * Removes the given prefreence node if it is managed by
+     * this contribution manager.
+     *
+     * @param node the node to remove
+     * @return <code>true</code> if the node was removed,
+     *  and <code>false</code> otherwise
+     */
+    public bool remove(IPreferenceNode node) {
+        Assert.isNotNull(cast(Object)node);
+
+        return root.remove(node);
+    }
+
+    /**
+     * Removes all contribution nodes known to this manager.
+     */
+    public void removeAll() {
+        root = new PreferenceNode("");//$NON-NLS-1$
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/PreferenceNode.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,290 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.preference.PreferenceNode;
+
+import dwtx.jface.preference.IPreferenceNode;
+import dwtx.jface.preference.IPreferencePage;
+
+
+import tango.util.collection.ArraySeq;
+// import java.util.List;
+
+import dwt.graphics.Image;
+import dwtx.core.runtime.Assert;
+import dwtx.jface.resource.ImageDescriptor;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A concrete implementation of a node in a preference dialog tree. This class
+ * also supports lazy creation of the node's preference page.
+ */
+public class PreferenceNode : IPreferenceNode {
+    /**
+     * Preference page, or <code>null</code> if not yet loaded.
+     */
+    private IPreferencePage page;
+
+    /**
+     * The list of subnodes (immediate children) of this node (element type:
+     * <code>IPreferenceNode</code>).
+     */
+    private ArraySeq!(Object) subNodes;
+
+    /**
+     * Name of a class that implements <code>IPreferencePage</code>, or
+     * <code>null</code> if none.
+     */
+    private String classname;
+
+    /**
+     * The id of this node.
+     */
+    private String id;
+
+    /**
+     * Text label for this node. Note that this field is only used prior to the
+     * creation of the preference page.
+     */
+    private String label;
+
+    /**
+     * Image descriptor for this node, or <code>null</code> if none.
+     */
+    private ImageDescriptor imageDescriptor;
+
+    /**
+     * Cached image, or <code>null</code> if none.
+     */
+    private Image image;
+
+    /**
+     * Creates a new preference node with the given id. The new node has no
+     * subnodes.
+     *
+     * @param id
+     *            the node id
+     */
+    public this(String id) {
+        Assert.isNotNull(id);
+        this.id = id;
+    }
+
+    /**
+     * Creates a preference node with the given id, label, and image, and
+     * lazily-loaded preference page. The preference node assumes (sole)
+     * responsibility for disposing of the image; this will happen when the node
+     * is disposed.
+     *
+     * @param id
+     *            the node id
+     * @param label
+     *            the label used to display the node in the preference dialog's
+     *            tree
+     * @param image
+     *            the image displayed left of the label in the preference
+     *            dialog's tree, or <code>null</code> if none
+     * @param className
+     *            the class name of the preference page; this class must
+     *            implement <code>IPreferencePage</code>
+     */
+    public this(String id, String label, ImageDescriptor image,
+            String className) {
+        this(id);
+        this.imageDescriptor = image;
+        Assert.isNotNull(label);
+        this.label = label;
+        this.classname = className;
+    }
+
+    /**
+     * Creates a preference node with the given id and preference page. The
+     * title of the preference page is used for the node label. The node will
+     * not have an image.
+     *
+     * @param id
+     *            the node id
+     * @param preferencePage
+     *            the preference page
+     */
+    public this(String id, IPreferencePage preferencePage) {
+        this(id);
+        Assert.isNotNull(cast(Object)preferencePage);
+        page = preferencePage;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public void add(IPreferenceNode node) {
+        if (subNodes is null) {
+            subNodes = new ArraySeq!(Object);
+        }
+        subNodes.append(cast(Object)node);
+    }
+
+    /**
+     * Creates a new instance of the given class <code>className</code>.
+     *
+     * @param className
+     * @return new Object or <code>null</code> in case of failures.
+     */
+    private Object createObject(String className) {
+        Assert.isNotNull(className);
+//         try {
+            ClassInfo cl = ClassInfo.find(className);
+            if (cl !is null) {
+                return cl.create();
+            }
+//         } catch (ClassNotFoundException e) {
+//             return null;
+//         } catch (InstantiationException e) {
+//             return null;
+//         } catch (IllegalAccessException e) {
+//             return null;
+//         } catch (NoSuchMethodError e) {
+//             return null;
+//         }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public void createPage() {
+        page = cast(IPreferencePage) createObject(classname);
+        if (getLabelImage() !is null) {
+            page.setImageDescriptor(imageDescriptor);
+        }
+        page.setTitle(label);
+    }
+
+    /**
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public void disposeResources() {
+        if (image !is null) {
+            image.dispose();
+            image = null;
+        }
+        if (page !is null) {
+            page.dispose();
+            page = null;
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionNode.
+     */
+    public IPreferenceNode findSubNode(String id) {
+        Assert.isNotNull(id);
+        Assert.isTrue(id.length > 0);
+        if (subNodes is null) {
+            return null;
+        }
+        int size = subNodes.size();
+        for (int i = 0; i < size; i++) {
+            IPreferenceNode node = cast(IPreferenceNode) subNodes.get(i);
+            if (id.equals(node.getId())) {
+                return node;
+            }
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public String getId() {
+        return this.id;
+    }
+
+    /**
+     * Returns the image descriptor for this node.
+     *
+     * @return the image descriptor
+     */
+    protected ImageDescriptor getImageDescriptor() {
+        return imageDescriptor;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public Image getLabelImage() {
+        if (image is null && imageDescriptor !is null) {
+            image = imageDescriptor.createImage();
+        }
+        return image;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public String getLabelText() {
+        if (page !is null) {
+            return page.getTitle();
+        }
+        return label;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public IPreferencePage getPage() {
+        return page;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public IPreferenceNode[] getSubNodes() {
+        if (subNodes is null) {
+            return new IPreferenceNode[0];
+        }
+        return arraycast!(IPreferenceNode)( subNodes
+                .toArray());
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public IPreferenceNode remove(String id) {
+        IPreferenceNode node = findSubNode(id);
+        if (node !is null) {
+            remove(node);
+        }
+        return node;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceNode.
+     */
+    public bool remove(IPreferenceNode node) {
+        if (subNodes is null) {
+            return false;
+        }
+        bool res = subNodes.contains(cast(Object)node);
+        subNodes.remove(cast(Object)node);
+        return res;
+    }
+
+    /**
+     * Set the current page to be newPage.
+     *
+     * @param newPage
+     */
+    public void setPage(IPreferencePage newPage) {
+        page = newPage;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/PreferencePage.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,662 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Sebastian Davids <sdavids@gmx.de> - Fix for bug 38729 - [Preferences]
+ *           NPE PreferencePage isValid.
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.preference.PreferencePage;
+
+import dwtx.jface.preference.IPreferencePage;
+import dwtx.jface.preference.IPreferenceStore;
+import dwtx.jface.preference.IPreferencePageContainer;
+
+import dwt.DWT;
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.events.SelectionAdapter;
+import dwt.events.SelectionEvent;
+import dwt.graphics.Font;
+import dwt.graphics.Point;
+import dwt.layout.GridData;
+import dwt.layout.GridLayout;
+import dwt.widgets.Button;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Event;
+import dwt.widgets.Label;
+import dwtx.jface.dialogs.Dialog;
+import dwtx.jface.dialogs.DialogPage;
+import dwtx.jface.dialogs.IDialogConstants;
+import dwtx.jface.resource.ImageDescriptor;
+import dwtx.jface.resource.JFaceResources;
+import dwtx.jface.util.IPropertyChangeListener;
+import dwtx.jface.util.PropertyChangeEvent;
+
+import dwt.dwthelper.utils;
+
+/**
+ * Abstract base implementation for all preference page implementations.
+ * <p>
+ * Subclasses must implement the <code>createControl</code> framework
+ * method to supply the page's main control.
+ * </p>
+ * <p>
+ * Subclasses should extend the <code>doComputeSize</code> framework
+ * method to compute the size of the page's control.
+ * </p>
+ * <p>
+ * Subclasses may override the <code>performOk</code>, <code>performApply</code>,
+ * <code>performDefaults</code>, <code>performCancel</code>, and <code>performHelp</code>
+ * framework methods to react to the standard button events.
+ * </p>
+ * <p>
+ * Subclasses may call the <code>noDefaultAndApplyButton</code> framework
+ * method before the page's control has been created to suppress
+ * the standard Apply and Defaults buttons.
+ * </p>
+ */
+public abstract class PreferencePage : DialogPage,
+        IPreferencePage {
+
+    /**
+     * Preference store, or <code>null</code>.
+     */
+    private IPreferenceStore preferenceStore;
+
+    /**
+     * Valid state for this page; <code>true</code> by default.
+     *
+     * @see #isValid
+     */
+    private bool isValid_ = true;
+
+    /**
+     * Body of page.
+     */
+    private Control body_;
+
+    /**
+     * Whether this page has the standard Apply and Defaults buttons;
+     * <code>true</code> by default.
+     *
+     * @see #noDefaultAndApplyButton
+     */
+    private bool createDefaultAndApplyButton = true;
+
+    /**
+     * Standard Defaults button, or <code>null</code> if none.
+     * This button has id <code>DEFAULTS_ID</code>.
+     */
+    private Button defaultsButton = null;
+
+    /**
+     * The container this preference page belongs to; <code>null</code>
+     * if none.
+     */
+    private IPreferencePageContainer container = null;
+
+    /**
+     * Standard Apply button, or <code>null</code> if none.
+     * This button has id <code>APPLY_ID</code>.
+     */
+    private Button applyButton = null;
+
+    /**
+     * Description label.
+     *
+     * @see #createDescriptionLabel(Composite)
+     */
+    private Label descriptionLabel;
+
+    /**
+     * Caches size of page.
+     */
+    private Point size = null;
+
+
+    /**
+     * Creates a new preference page with an empty title and no image.
+     */
+    protected this() {
+        this(""); //$NON-NLS-1$
+    }
+
+    /**
+     * Creates a new preference page with the given title and no image.
+     *
+     * @param title the title of this preference page
+     */
+    protected this(String title) {
+        super(title);
+    }
+
+    /**
+     * Creates a new abstract preference page with the given title and image.
+     *
+     * @param title the title of this preference page
+     * @param image the image for this preference page,
+     *  or <code>null</code> if none
+     */
+    protected this(String title, ImageDescriptor image) {
+        super(title, image);
+    }
+
+    /**
+     * Computes the size for this page's UI control.
+     * <p>
+     * The default implementation of this <code>IPreferencePage</code>
+     * method returns the size set by <code>setSize</code>; if no size
+     * has been set, but the page has a UI control, the framework
+     * method <code>doComputeSize</code> is called to compute the size.
+     * </p>
+     *
+     * @return the size of the preference page encoded as
+     *   <code>new Point(width,height)</code>, or
+     *   <code>(0,0)</code> if the page doesn't currently have any UI component
+     */
+    public Point computeSize() {
+        if (size !is null) {
+            return size;
+        }
+        Control control = getControl();
+        if (control !is null) {
+            size = doComputeSize();
+            return size;
+        }
+        return new Point(0, 0);
+    }
+
+    /**
+     * Contributes additional buttons to the given composite.
+     * <p>
+     * The default implementation of this framework hook method does
+     * nothing. Subclasses should override this method to contribute buttons
+     * to this page's button bar. For each button a subclass contributes,
+     * it must also increase the parent's grid layout number of columns
+     * by one; that is,
+     * <pre>
+     * ((GridLayout) parent.getLayout()).numColumns++);
+     * </pre>
+     * </p>
+     *
+     * @param parent the button bar
+     */
+    protected void contributeButtons(Composite parent) {
+    }
+
+    /**
+     * Creates and returns the DWT control for the customized body
+     * of this preference page under the given parent composite.
+     * <p>
+     * This framework method must be implemented by concrete subclasses. Any
+     * subclass returning a <code>Composite</code> object whose <code>Layout</code>
+     * has default margins (for example, a <code>GridLayout</code>) are expected to
+     * set the margins of this <code>Layout</code> to 0 pixels.
+     * </p>
+     *
+     * @param parent the parent composite
+     * @return the new control
+     */
+    protected abstract Control createContents(Composite parent);
+
+    /**
+     * The <code>PreferencePage</code> implementation of this
+     * <code>IDialogPage</code> method creates a description label
+     * and button bar for the page. It calls <code>createContents</code>
+     * to create the custom contents of the page.
+     * <p>
+     * If a subclass that overrides this method creates a <code>Composite</code>
+     * that has a layout with default margins (for example, a <code>GridLayout</code>)
+     * it is expected to set the margins of this <code>Layout</code> to 0 pixels.
+     */
+    public void createControl(Composite parent){
+
+        GridData gd;
+        Composite content = new Composite(parent, DWT.NONE);
+        setControl(content);
+        GridLayout layout = new GridLayout();
+        layout.marginWidth = 0;
+        layout.marginHeight = 0;
+        content.setLayout(layout);
+        //Apply the font on creation for backward compatibility
+        applyDialogFont(content);
+
+        // initialize the dialog units
+        initializeDialogUnits(content);
+
+        descriptionLabel = createDescriptionLabel(content);
+        if (descriptionLabel !is null) {
+            descriptionLabel.setLayoutData(new GridData(
+                    GridData.FILL_HORIZONTAL));
+        }
+
+        body_ = createContents(content);
+        if (body_ !is null) {
+            // null is not a valid return value but support graceful failure
+            body_.setLayoutData(new GridData(GridData.FILL_BOTH));
+        }
+
+        Composite buttonBar = new Composite(content, DWT.NONE);
+        layout = new GridLayout();
+        layout.numColumns = 0;
+        layout.marginHeight = 0;
+        layout.marginWidth = 0;
+        layout.makeColumnsEqualWidth = false;
+        buttonBar.setLayout(layout);
+
+        gd = new GridData(GridData.HORIZONTAL_ALIGN_END);
+
+        buttonBar.setLayoutData(gd);
+
+        contributeButtons(buttonBar);
+
+        if (createDefaultAndApplyButton) {
+            layout.numColumns = layout.numColumns + 2;
+            String[] labels = JFaceResources.getStrings([
+                    "defaults", "apply"]); //$NON-NLS-2$//$NON-NLS-1$
+            int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
+            defaultsButton = new Button(buttonBar, DWT.PUSH);
+            defaultsButton.setText(labels[0]);
+            Dialog.applyDialogFont(defaultsButton);
+            GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+            Point minButtonSize = defaultsButton.computeSize(DWT.DEFAULT,
+                    DWT.DEFAULT, true);
+            data.widthHint = Math.max(widthHint, minButtonSize.x);
+            defaultsButton.setLayoutData(data);
+            defaultsButton.addSelectionListener(new class SelectionAdapter {
+                public void widgetSelected(SelectionEvent e) {
+                    performDefaults();
+                }
+            });
+
+            applyButton = new Button(buttonBar, DWT.PUSH);
+            applyButton.setText(labels[1]);
+            Dialog.applyDialogFont(applyButton);
+            data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+            minButtonSize = applyButton.computeSize(DWT.DEFAULT, DWT.DEFAULT,
+                    true);
+            data.widthHint = Math.max(widthHint, minButtonSize.x);
+            applyButton.setLayoutData(data);
+            applyButton.addSelectionListener(new class SelectionAdapter {
+                public void widgetSelected(SelectionEvent e) {
+                    performApply();
+                }
+            });
+            applyButton.setEnabled(isValid());
+            applyDialogFont(buttonBar);
+        } else {
+            /* Check if there are any other buttons on the button bar.
+             * If not, throw away the button bar composite.  Otherwise
+             * there is an unusually large button bar.
+             */
+            if (buttonBar.getChildren().length < 1) {
+                buttonBar.dispose();
+            }
+        }
+    }
+
+
+
+    /**
+     * Apply the dialog font to the composite and it's children
+     * if it is set. Subclasses may override if they wish to
+     * set the font themselves.
+     * @param composite
+     */
+    protected void applyDialogFont(Composite composite) {
+        Dialog.applyDialogFont(composite);
+    }
+
+    /**
+     * Creates and returns an DWT label under the given composite.
+     *
+     * @param parent the parent composite
+     * @return the new label
+     */
+    protected Label createDescriptionLabel(Composite parent) {
+        Label result = null;
+        String description = getDescription();
+        if (description !is null) {
+            result = new Label(parent, DWT.WRAP);
+            result.setFont(parent.getFont());
+            result.setText(description);
+        }
+        return result;
+    }
+
+    /**
+     * Computes the size needed by this page's UI control.
+     * <p>
+     * All pages should override this method and set the appropriate sizes
+     * of their widgets, and then call <code>super.doComputeSize</code>.
+     * </p>
+     *
+     * @return the size of the preference page encoded as
+     *   <code>new Point(width,height)</code>
+     */
+    protected Point doComputeSize() {
+        if (descriptionLabel !is null && body_ !is null) {
+            Point bodySize = body_.computeSize(DWT.DEFAULT, DWT.DEFAULT, true);
+            GridData gd = cast(GridData) descriptionLabel.getLayoutData();
+            gd.widthHint = bodySize.x;
+        }
+        return getControl().computeSize(DWT.DEFAULT, DWT.DEFAULT, true);
+    }
+
+    /**
+     * Returns the preference store of this preference page.
+     * <p>
+     * This is a framework hook method for subclasses to return a
+     * page-specific preference store. The default implementation
+     * returns <code>null</code>.
+     * </p>
+     *
+     * @return the preference store, or <code>null</code> if none
+     */
+    protected IPreferenceStore doGetPreferenceStore() {
+        return null;
+    }
+
+    /**
+     * Returns the container of this page.
+     *
+     * @return the preference page container, or <code>null</code> if this
+     *   page has yet to be added to a container
+     */
+    public IPreferencePageContainer getContainer() {
+        return container;
+    }
+
+    /**
+     * Returns the preference store of this preference page.
+     *
+     * @return the preference store , or <code>null</code> if none
+     */
+    public IPreferenceStore getPreferenceStore() {
+        if (preferenceStore is null) {
+            preferenceStore = doGetPreferenceStore();
+        }
+        if (preferenceStore !is null) {
+            return preferenceStore;
+        } else if (container !is null) {
+            return container.getPreferenceStore();
+        }
+        return null;
+    }
+
+    /**
+     * The preference page implementation of an <code>IPreferencePage</code>
+     * method returns whether this preference page is valid. Preference
+     * pages are considered valid by default; call <code>setValid(false)</code>
+     * to make a page invalid.
+     */
+    public bool isValid() {
+        return isValid_;
+    }
+
+    /**
+     * Suppresses creation of the standard Default and Apply buttons
+     * for this page.
+     * <p>
+     * Subclasses wishing a preference page wihthout these buttons
+     * should call this framework method before the page's control
+     * has been created.
+     * </p>
+     */
+    protected void noDefaultAndApplyButton() {
+        createDefaultAndApplyButton = false;
+    }
+
+    /**
+     * The <code>PreferencePage</code> implementation of this
+     * <code>IPreferencePage</code> method returns <code>true</code>
+     * if the page is valid.
+     */
+    public bool okToLeave() {
+        return isValid();
+    }
+
+    /**
+     * Performs special processing when this page's Apply button has been pressed.
+     * <p>
+     * This is a framework hook method for sublcasses to do special things when
+     * the Apply button has been pressed.
+     * The default implementation of this framework method simply calls
+     * <code>performOk</code> to simulate the pressing of the page's OK button.
+     * </p>
+     *
+     * @see #performOk
+     */
+    protected void performApply() {
+        performOk();
+    }
+
+    /**
+     * The preference page implementation of an <code>IPreferencePage</code>
+     * method performs special processing when this page's Cancel button has
+     * been pressed.
+     * <p>
+     * This is a framework hook method for sublcasses to do special things when
+     * the Cancel button has been pressed. The default implementation of this
+     * framework method does nothing and returns <code>true</code>.
+     */
+    public bool performCancel() {
+        return true;
+    }
+
+    /**
+     * Performs special processing when this page's Defaults button has been pressed.
+     * <p>
+     * This is a framework hook method for subclasses to do special things when
+     * the Defaults button has been pressed.
+     * Subclasses may override, but should call <code>super.performDefaults</code>.
+     * </p>
+     */
+    protected void performDefaults() {
+        updateApplyButton();
+    }
+
+    /**
+     * Method declared on IPreferencePage.
+     * Subclasses should override
+     */
+    public bool performOk() {
+        return true;
+    }
+
+    /** (non-Javadoc)
+     * Method declared on IPreferencePage.
+     */
+    public void setContainer(IPreferencePageContainer container) {
+        this.container = container;
+    }
+
+    /**
+     * Sets the preference store for this preference page.
+     * <p>
+     * If preferenceStore is set to null, getPreferenceStore
+     * will invoke doGetPreferenceStore the next time it is called.
+     * </p>
+     *
+     * @param store the preference store, or <code>null</code>
+     * @see #getPreferenceStore
+     */
+    public void setPreferenceStore(IPreferenceStore store) {
+        preferenceStore = store;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on IPreferencePage.
+     */
+    public void setSize(Point uiSize) {
+        Control control = getControl();
+        if (control !is null) {
+            control.setSize(uiSize);
+            size = uiSize;
+        }
+    }
+
+    /**
+     * The <code>PreferencePage</code> implementation of this <code>IDialogPage</code>
+     * method extends the <code>DialogPage</code> implementation to update
+     * the preference page container title. Subclasses may extend.
+     */
+    public void setTitle(String title) {
+        super.setTitle(title);
+        if (getContainer() !is null) {
+            getContainer().updateTitle();
+        }
+    }
+
+    /**
+     * Sets whether this page is valid.
+     * The enable state of the container buttons and the
+     * apply button is updated when a page's valid state
+     * changes.
+     * <p>
+     *
+     * @param b the new valid state
+     */
+    public void setValid(bool b) {
+        bool oldValue = isValid_;
+        isValid_ = b;
+        if (oldValue !is isValid_) {
+            // update container state
+            if (getContainer() !is null) {
+                getContainer().updateButtons();
+            }
+            // update page state
+            updateApplyButton();
+        }
+    }
+
+    /**
+     * Returns a string suitable for debugging purpose only.
+     */
+    public String toString() {
+        return getTitle();
+    }
+
+    /**
+     * Updates the enabled state of the Apply button to reflect whether
+     * this page is valid.
+     */
+    protected void updateApplyButton() {
+        if (applyButton !is null) {
+            applyButton.setEnabled(isValid());
+        }
+    }
+
+    /**
+     * Creates a composite with a highlighted Note entry and a message text.
+     * This is designed to take up the full width of the page.
+     *
+     * @param font the font to use
+     * @param composite the parent composite
+     * @param title the title of the note
+     * @param message the message for the note
+     * @return the composite for the note
+     */
+    protected Composite createNoteComposite(Font font, Composite composite,
+            String title, String message) {
+        Composite messageComposite = new Composite(composite, DWT.NONE);
+        GridLayout messageLayout = new GridLayout();
+        messageLayout.numColumns = 2;
+        messageLayout.marginWidth = 0;
+        messageLayout.marginHeight = 0;
+        messageComposite.setLayout(messageLayout);
+        messageComposite.setLayoutData(new GridData(
+                GridData.HORIZONTAL_ALIGN_FILL));
+        messageComposite.setFont(font);
+
+        final Label noteLabel = new Label(messageComposite, DWT.BOLD);
+        noteLabel.setText(title);
+        noteLabel.setFont(JFaceResources.getFontRegistry().getBold(
+                JFaceResources.DEFAULT_FONT));
+        noteLabel
+                .setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING));
+
+        final IPropertyChangeListener fontListener = new class IPropertyChangeListener {
+            public void propertyChange(PropertyChangeEvent event) {
+                if (JFaceResources.BANNER_FONT.equals(event.getProperty())) {
+                    noteLabel.setFont(JFaceResources
+                            .getFont(JFaceResources.BANNER_FONT));
+                }
+            }
+        };
+        JFaceResources.getFontRegistry().addListener(fontListener);
+        noteLabel.addDisposeListener(new class DisposeListener {
+            public void widgetDisposed(DisposeEvent event) {
+                JFaceResources.getFontRegistry().removeListener(fontListener);
+            }
+        });
+
+        Label messageLabel = new Label(messageComposite, DWT.WRAP);
+        messageLabel.setText(message);
+        messageLabel.setFont(font);
+        return messageComposite;
+    }
+
+    /**
+     * Returns the Apply button.
+     *
+     * @return the Apply button
+     */
+    protected Button getApplyButton() {
+        return applyButton;
+    }
+
+    /**
+     * Returns the Restore Defaults button.
+     *
+     * @return the Restore Defaults button
+     */
+    protected Button getDefaultsButton() {
+        return defaultsButton;
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.dialogs.IDialogPage#performHelp()
+     */
+    public void performHelp() {
+        getControl().notifyListeners(DWT.Help, new Event());
+    }
+
+    /**
+     * Apply the data to the receiver. By default do nothing.
+     * @param data
+     * @since 3.1
+     */
+    public void applyData(Object data) {
+
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.dialogs.DialogPage#setErrorMessage(java.lang.String)
+     */
+    public void setErrorMessage(String newMessage) {
+        super.setErrorMessage(newMessage);
+        if (getContainer() !is null) {
+            getContainer().updateMessage();
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.dialogs.DialogPage#setMessage(java.lang.String, int)
+     */
+    public void setMessage(String newMessage, int newType) {
+        super.setMessage(newMessage, newType);
+        if (getContainer() !is null) {
+            getContainer().updateMessage();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/PreferenceStore.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,937 @@
+/*******************************************************************************
+ * 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.preference.PreferenceStore;
+
+import dwtx.jface.preference.IPersistentPreferenceStore;
+import dwtx.jface.preference.IPreferenceStore;
+
+// import java.io.FileInputStream;
+// import java.io.FileOutputStream;
+// import java.io.IOException;
+// import java.io.InputStream;
+// import java.io.OutputStream;
+// import java.io.PrintStream;
+// import java.io.PrintWriter;
+// import java.util.ArrayList;
+// import java.util.Enumeration;
+// import java.util.Properties;
+import tango.util.collection.ArraySeq;
+
+import dwtx.core.commands.common.EventManager;
+import dwtx.core.runtime.Assert;
+import dwtx.jface.resource.JFaceResources;
+import dwtx.jface.util.IPropertyChangeListener;
+import dwtx.jface.util.PropertyChangeEvent;
+import dwtx.jface.util.SafeRunnable;
+
+import dwt.dwthelper.utils;
+import tango.io.Print;
+import tango.io.stream.FileStream;
+
+    import tango.io.model.IConduit;
+    import tango.text.Util;
+    public class Properties {
+
+        protected Properties defaults;
+
+        private String[ String ] map;
+
+        public this (){
+        }
+
+        public this ( Properties defaults ){
+            this.defaults = defaults;
+        }
+
+        public synchronized String setProperty( String key, String value ){
+            String res;
+            if( auto v = key in map ){
+                res = *v;
+            }
+            map[ key ] = value;
+            return res;
+        }
+
+        public synchronized void load( InputStream inStream ){
+            char[] line;
+            bool eof = false;
+            //tango.io.Stdout.Stdout.formatln( "properties put ..." );
+            void readLine(){
+                line.length = 0;
+                char[1] rdbuf;
+                int i = inStream.read( rdbuf );
+                while( i is 1 && rdbuf[0] != '\n' && rdbuf[0] != '\r' ){
+                    line ~= rdbuf[0];
+                    i = inStream.read( rdbuf );
+                }
+                eof = i !is 1;
+            }
+
+            //tango.io.Stdout.Stdout.formatln( "properties put {}", __LINE__ );
+            bool linecontinue = false;
+            bool iskeypart = true;
+            char[] key;
+            char[] value;
+    nextline:
+            while( !eof ){
+                //tango.io.Stdout.Stdout.formatln( "properties put {} startline", __LINE__ );
+                readLine();
+                line = tango.text.Util.trim( line );
+                if( line.length == 0 ){
+                    //tango.io.Stdout.Stdout.formatln( "properties put {} was 0 length", __LINE__ );
+                    continue;
+                }
+                if( line[0] == '#' ){
+                    //tango.io.Stdout.Stdout.formatln( "properties put {} was comment", __LINE__ );
+                    continue;
+                }
+                int pos = 0;
+                bool esc = false;
+                if( !linecontinue ){
+                    iskeypart = true;
+                    key = null;
+                    value = null;
+                }
+                else{
+                    linecontinue = false;
+                }
+                while( pos < line.length ){
+                    char c = line[pos];
+                    if( esc ){
+                        esc = false;
+                        switch( c ){
+                        case 't': c = '\t'; break;
+                        case 'n': c = '\n'; break;
+                        case '\\': c = '\\'; break;
+                        default:  c = '?'; break;
+                        }
+                    }
+                    else{
+                        if( c == '\\' ){
+                            if( pos == line.length -1 ){
+                                linecontinue = true;
+                                goto nextline;
+                            }
+                            esc = true;
+                            pos++;
+                            continue;
+                        }
+                        else if( iskeypart && c == '=' ){
+                            pos++;
+                            iskeypart = false;
+                            continue;
+                        }
+                    }
+                    pos++;
+                    if( iskeypart ){
+                        key ~= c;
+                    }
+                    else{
+                        value ~= c;
+                    }
+                }
+                if( iskeypart ){
+                    tango.io.Stdout.Stdout.formatln( "dejavu.util.Properties put cannot find '='." );
+                    continue;
+                }
+                key = tango.text.Util.trim( key );
+                value = tango.text.Util.trim(value);
+                //tango.io.Stdout.Stdout.formatln( "properties put {}=>{}", key, value );
+
+                map[ key.dup ] = value.dup;
+                //tango.io.Stdout.Stdout.formatln( "properties put {}", __LINE__ );
+            }
+        }
+
+        public synchronized void store( OutputStream out_, String comments ){
+            implMissing( __FILE__, __LINE__ );
+        }
+
+//         public synchronized void save( dejavu.io.OutputStream.OutputStream out_KEYWORDESCAPE, dejavu.lang.String.String comments ){
+//             implMissing( __FILE__, __LINE__ );
+//         }
+//
+//         public synchronized void loadFromXML( dejavu.io.InputStream.InputStream in_KEYWORDESCAPE ){
+//             implMissing( __FILE__, __LINE__ );
+//         }
+//
+//         public synchronized void storeToXML( dejavu.io.OutputStream.OutputStream os, dejavu.lang.String.String comment ){
+//             implMissing( __FILE__, __LINE__ );
+//         }
+//
+//         public synchronized void storeToXML( dejavu.io.OutputStream.OutputStream os, dejavu.lang.String.String comment, dejavu.lang.String.String encoding ){
+//             implMissing( __FILE__, __LINE__ );
+//         }
+
+        public String getProperty( String aKey ){
+            if( auto res = aKey in map ){
+                return *res;
+            }
+            if( defaults !is null ){
+                return defaults.getProperty( aKey );
+            }
+            return null;
+        }
+
+        public String get( String aKey ){
+            if( auto res = aKey in map ){
+                return *res;
+            }
+            return null;
+        }
+        public String put( String aKey, String aValue ){
+            if( auto pres = aKey in map ){
+                String res = *pres;
+                map[ aKey ] = aValue;
+                return res;
+            }
+            map[ aKey ] = aValue;
+            return null;
+        }
+        public String remove( String aKey ){
+            if( auto res = aKey in map ){
+                map.remove(aKey);
+                return *res;
+            }
+            return null;
+        }
+
+        public String getProperty( String key, String defaultValue ){
+            if( auto res = key in map ){
+                return *res;
+            }
+            return defaultValue;
+        }
+
+        public void list(Print!(char) print){
+            foreach( k, v; map ){
+                print( k )( '=' )( v ).newline;
+            }
+        }
+        public bool containsKey( String key ){
+            return ( key in map ) !is null;
+        }
+        public String[] propertyNames(){
+            String[] res = new String[ map.length ];
+            int idx;
+            foreach( key, val; map ){
+                res[idx] = key;
+                idx++;
+            }
+            return res;
+        }
+
+//         public dejavu.util.Enumeration.Enumeration propertyNames(){
+//             implMissing( __FILE__, __LINE__ );
+//             return null;
+//         }
+//
+//         public void list( dejavu.io.PrintStream.PrintStream out_KEYWORDESCAPE ){
+//             implMissing( __FILE__, __LINE__ );
+//         }
+//
+//         public void list( dejavu.io.PrintWriter.PrintWriter out_KEYWORDESCAPE ){
+//             implMissing( __FILE__, __LINE__ );
+//         }
+//
+//         public override char[] toUtf8(){
+//             return "";
+//         }
+
+    }
+
+
+
+/**
+ * A concrete preference store implementation based on an internal
+ * <code>java.util.Properties</code> object, with support for persisting the
+ * non-default preference values to files or streams.
+ * <p>
+ * This class was not designed to be subclassed.
+ * </p>
+ *
+ * @see IPreferenceStore
+ */
+public class PreferenceStore : EventManager,
+        IPersistentPreferenceStore {
+
+    /**
+     * The mapping from preference name to preference value (represented as
+     * strings).
+     */
+    private Properties properties;
+
+    /**
+     * The mapping from preference name to default preference value (represented
+     * as strings); <code>null</code> if none.
+     */
+    private Properties defaultProperties;
+
+    /**
+     * Indicates whether a value as been changed by <code>setToDefault</code>
+     * or <code>setValue</code>; initially <code>false</code>.
+     */
+    private bool dirty = false;
+
+    /**
+     * The file name used by the <code>load</code> method to load a property
+     * file. This filename is used to save the properties file when
+     * <code>save</code> is called.
+     */
+    private String filename;
+
+    /**
+     * Creates an empty preference store.
+     * <p>
+     * Use the methods <code>load(InputStream)</code> and
+     * <code>save(InputStream)</code> to load and store this preference store.
+     * </p>
+     *
+     * @see #load(InputStream)
+     * @see #save(OutputStream, String)
+     */
+    public this() {
+        defaultProperties = new Properties();
+        properties = new Properties(defaultProperties);
+    }
+
+    /**
+     * Creates an empty preference store that loads from and saves to the a
+     * file.
+     * <p>
+     * Use the methods <code>load()</code> and <code>save()</code> to load
+     * and store this preference store.
+     * </p>
+     *
+     * @param filename
+     *            the file name
+     * @see #load()
+     * @see #save()
+     */
+    public this(String filename) {
+        this();
+        Assert.isNotNull(filename);
+        this.filename = filename;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void addPropertyChangeListener(IPropertyChangeListener listener) {
+        addListenerObject(cast(Object)listener);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public bool contains(String name) {
+        return (properties.containsKey(name) || defaultProperties
+                .containsKey(name));
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void firePropertyChangeEvent(String name, Object oldValue,
+            Object newValue) {
+        final Object[] finalListeners = getListeners();
+        // Do we need to fire an event.
+        if (finalListeners.length > 0
+                && (oldValue is null || !oldValue.opEquals(newValue))) {
+            final PropertyChangeEvent pe = new PropertyChangeEvent(this, name,
+                    oldValue, newValue);
+            for (int i = 0; i < finalListeners.length; ++i) {
+                SafeRunnable.run(new class(JFaceResources.getString("PreferenceStore.changeError")) SafeRunnable { //$NON-NLS-1$
+                    IPropertyChangeListener l;
+                    this(char[] s){
+                        super(s);
+                        l = cast(IPropertyChangeListener) finalListeners[i];
+                    }
+                    public void run() {
+                        l.propertyChange(pe);
+                    }
+                });
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public bool getBoolean(String name) {
+        return getBoolean(properties, name);
+    }
+
+    /**
+     * Helper function: gets bool for a given name.
+     *
+     * @param p
+     * @param name
+     * @return bool
+     */
+    private bool getBoolean(Properties p, String name) {
+        String value = p !is null ? p.getProperty(name) : null;
+        if (value is null) {
+            return BOOLEAN_DEFAULT_DEFAULT;
+        }
+        if (value.equals(IPreferenceStore.TRUE)) {
+            return true;
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public bool getDefaultBoolean(String name) {
+        return getBoolean(defaultProperties, name);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public double getDefaultDouble(String name) {
+        return getDouble(defaultProperties, name);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public float getDefaultFloat(String name) {
+        return getFloat(defaultProperties, name);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public int getDefaultInt(String name) {
+        return getInt(defaultProperties, name);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public long getDefaultLong(String name) {
+        return getLong(defaultProperties, name);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public String getDefaultString(String name) {
+        return getString(defaultProperties, name);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public double getDouble(String name) {
+        return getDouble(properties, name);
+    }
+
+    /**
+     * Helper function: gets double for a given name.
+     *
+     * @param p
+     * @param name
+     * @return double
+     */
+    private double getDouble(Properties p, String name) {
+        String value = p !is null ? p.getProperty(name) : null;
+        if (value is null) {
+            return DOUBLE_DEFAULT_DEFAULT;
+        }
+        double ival = DOUBLE_DEFAULT_DEFAULT;
+        try {
+            ival = (new Double(value)).doubleValue();
+        } catch (NumberFormatException e) {
+        }
+        return ival;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public float getFloat(String name) {
+        return getFloat(properties, name);
+    }
+
+    /**
+     * Helper function: gets float for a given name.
+     *
+     * @param p
+     * @param name
+     * @return float
+     */
+    private float getFloat(Properties p, String name) {
+        String value = p !is null ? p.getProperty(name) : null;
+        if (value is null) {
+            return FLOAT_DEFAULT_DEFAULT;
+        }
+        float ival = FLOAT_DEFAULT_DEFAULT;
+        try {
+            ival = (new Float(value)).floatValue();
+        } catch (NumberFormatException e) {
+        }
+        return ival;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public int getInt(String name) {
+        return getInt(properties, name);
+    }
+
+    /**
+     * Helper function: gets int for a given name.
+     *
+     * @param p
+     * @param name
+     * @return int
+     */
+    private int getInt(Properties p, String name) {
+        String value = p !is null ? p.getProperty(name) : null;
+        if (value is null) {
+            return INT_DEFAULT_DEFAULT;
+        }
+        int ival = 0;
+        try {
+            ival = Integer.parseInt(value);
+        } catch (NumberFormatException e) {
+        }
+        return ival;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public long getLong(String name) {
+        return getLong(properties, name);
+    }
+
+    /**
+     * Helper function: gets long for a given name.
+     *
+     * @param p
+     *            the properties storage (may be <code>null</code>)
+     * @param name
+     *            the name of the property
+     * @return the long or a default value of if:
+     *         <ul>
+     *         <li>properties storage is <code>null</code></li>
+     *         <li>property is not found</li>
+     *         <li>property value is not a number</li>
+     *         </ul>
+     * @see IPreferenceStore#LONG_DEFAULT_DEFAULT
+     */
+    private long getLong(Properties p, String name) {
+        String value = p !is null ? p.getProperty(name) : null;
+        if (value is null) {
+            return LONG_DEFAULT_DEFAULT;
+        }
+        long ival = LONG_DEFAULT_DEFAULT;
+        try {
+            ival = Long.parseLong(value);
+        } catch (NumberFormatException e) {
+        }
+        return ival;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public String getString(String name) {
+        return getString(properties, name);
+    }
+
+    /**
+     * Helper function: gets string for a given name.
+     *
+     * @param p
+     *            the properties storage (may be <code>null</code>)
+     * @param name
+     *            the name of the property
+     * @return the value or a default value of if:
+     *         <ul>
+     *         <li>properties storage is <code>null</code></li>
+     *         <li>property is not found</li>
+     *         <li>property value is not a number</li>
+     *         </ul>
+     * @see IPreferenceStore#STRING_DEFAULT_DEFAULT
+     */
+    private String getString(Properties p, String name) {
+        String value = p !is null ? p.getProperty(name) : null;
+        if (value is null) {
+            return STRING_DEFAULT_DEFAULT;
+        }
+        return value;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public bool isDefault(String name) {
+        return (!properties.containsKey(name) && defaultProperties
+                .containsKey(name));
+    }
+
+    /**
+     * Prints the contents of this preference store to the given print stream.
+     *
+     * @param out
+     *            the print stream
+     */
+    public void list(Print!(char) out_) {
+        properties.list(out_);
+    }
+
+//     /**
+//      * Prints the contents of this preference store to the given print writer.
+//      *
+//      * @param out
+//      *            the print writer
+//      */
+//     public void list(PrintWriter out_) {
+//         properties.list(out_);
+//     }
+
+    /**
+     * Loads this preference store from the file established in the constructor
+     * <code>PreferenceStore(java.lang.String)</code> (or by
+     * <code>setFileName</code>). Default preference values are not affected.
+     *
+     * @exception java.io.IOException
+     *                if there is a problem loading this store
+     */
+    public void load() {
+        if (filename is null) {
+            throw new IOException("File name not specified");//$NON-NLS-1$
+        }
+        FileInput in_ = new FileInput(filename);
+        load(in_.input);
+        in_.close();
+    }
+
+    /**
+     * Loads this preference store from the given input stream. Default
+     * preference values are not affected.
+     *
+     * @param in
+     *            the input stream
+     * @exception java.io.IOException
+     *                if there is a problem loading this store
+     */
+    public void load(InputStream in_)  {
+        properties.load(in_);
+        dirty = false;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public bool needsSaving() {
+        return dirty;
+    }
+
+    /**
+     * Returns an enumeration of all preferences known to this store which have
+     * current values other than their default value.
+     *
+     * @return an array of preference names
+     */
+    public String[] preferenceNames() {
+        String[] list;
+        foreach( prop; properties.propertyNames() ){
+            list ~= prop;
+        }
+        return list;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void putValue(String name, String value) {
+        String oldValue = getString(name);
+        if (oldValue is null || !oldValue.equals(value)) {
+            setValue(properties, name, value);
+            dirty = true;
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void removePropertyChangeListener(IPropertyChangeListener listener) {
+        removeListenerObject(cast(Object)listener);
+    }
+
+    /**
+     * Saves the non-default-valued preferences known to this preference store
+     * to the file from which they were originally loaded.
+     *
+     * @exception java.io.IOException
+     *                if there is a problem saving this store
+     */
+    public void save() {
+        if (filename is null) {
+            throw new IOException("File name not specified");//$NON-NLS-1$
+        }
+        FileOutput out_ = null;
+        try {
+            out_ = new FileOutput(filename);
+            save(out_, null);
+        } finally {
+            if (out_ !is null) {
+                out_.close();
+            }
+        }
+    }
+
+    /**
+     * Saves this preference store to the given output stream. The given string
+     * is inserted as header information.
+     *
+     * @param out
+     *            the output stream
+     * @param header
+     *            the header
+     * @exception java.io.IOException
+     *                if there is a problem saving this store
+     */
+    public void save(OutputStream out_, String header) {
+        properties.store(out_, header);
+        dirty = false;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setDefault(String name, double value) {
+        setValue(defaultProperties, name, value);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setDefault(String name, float value) {
+        setValue(defaultProperties, name, value);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setDefault(String name, int value) {
+        setValue(defaultProperties, name, value);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setDefault(String name, long value) {
+        setValue(defaultProperties, name, value);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setDefault(String name, String value) {
+        setValue(defaultProperties, name, value);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setDefault(String name, bool value) {
+        setValue(defaultProperties, name, value);
+    }
+
+    /**
+     * Sets the name of the file used when loading and storing this preference
+     * store.
+     * <p>
+     * Afterward, the methods <code>load()</code> and <code>save()</code>
+     * can be used to load and store this preference store.
+     * </p>
+     *
+     * @param name
+     *            the file name
+     * @see #load()
+     * @see #save()
+     */
+    public void setFilename(String name) {
+        filename = name;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setToDefault(String name) {
+        String oldValue = properties.get(name);
+        properties.remove(name);
+        dirty = true;
+        String newValue;
+        if (defaultProperties !is null) {
+            newValue = defaultProperties.get(name);
+        }
+        firePropertyChangeEvent(name, stringcast(oldValue), stringcast(newValue));
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setValue(String name, double value) {
+        double oldValue = getDouble(name);
+        if (oldValue !is value) {
+            setValue(properties, name, value);
+            dirty = true;
+            firePropertyChangeEvent(name, new Double(oldValue), new Double(
+                    value));
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setValue(String name, float value) {
+        float oldValue = getFloat(name);
+        if (oldValue !is value) {
+            setValue(properties, name, value);
+            dirty = true;
+            firePropertyChangeEvent(name, new Float(oldValue), new Float(value));
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setValue(String name, int value) {
+        int oldValue = getInt(name);
+        if (oldValue !is value) {
+            setValue(properties, name, value);
+            dirty = true;
+            firePropertyChangeEvent(name, new Integer(oldValue), new Integer(
+                    value));
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setValue(String name, long value) {
+        long oldValue = getLong(name);
+        if (oldValue !is value) {
+            setValue(properties, name, value);
+            dirty = true;
+            firePropertyChangeEvent(name, new Long(oldValue), new Long(value));
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setValue(String name, String value) {
+        String oldValue = getString(name);
+        if (oldValue is null || !oldValue.equals(value)) {
+            setValue(properties, name, value);
+            dirty = true;
+            firePropertyChangeEvent(name, stringcast(oldValue), stringcast(value));
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IPreferenceStore.
+     */
+    public void setValue(String name, bool value) {
+        bool oldValue = getBoolean(name);
+        if (oldValue !is value) {
+            setValue(properties, name, value);
+            dirty = true;
+            firePropertyChangeEvent(name, oldValue ? Boolean.TRUE
+                    : Boolean.FALSE, value ? Boolean.TRUE : Boolean.FALSE);
+        }
+    }
+
+    /**
+     * Helper method: sets value for a given name.
+     *
+     * @param p
+     * @param name
+     * @param value
+     */
+    private void setValue(Properties p, String name, double value) {
+        Assert.isTrue(p !is null);
+        p.put(name, Double.toString(value));
+    }
+
+    /**
+     * Helper method: sets value for a given name.
+     *
+     * @param p
+     * @param name
+     * @param value
+     */
+    private void setValue(Properties p, String name, float value) {
+        Assert.isTrue(p !is null);
+        p.put(name, Float.toString(value));
+    }
+
+    /**
+     * Helper method: sets value for a given name.
+     *
+     * @param p
+     * @param name
+     * @param value
+     */
+    private void setValue(Properties p, String name, int value) {
+        Assert.isTrue(p !is null);
+        p.put(name, Integer.toString(value));
+    }
+
+    /**
+     * Helper method: sets the value for a given name.
+     *
+     * @param p
+     * @param name
+     * @param value
+     */
+    private void setValue(Properties p, String name, long value) {
+        Assert.isTrue(p !is null);
+        p.put(name, Long.toString(value));
+    }
+
+    /**
+     * Helper method: sets the value for a given name.
+     *
+     * @param p
+     * @param name
+     * @param value
+     */
+    private void setValue(Properties p, String name, String value) {
+        Assert.isTrue(p !is null && value !is null);
+        p.put(name, value);
+    }
+
+    /**
+     * Helper method: sets the value for a given name.
+     *
+     * @param p
+     * @param name
+     * @param value
+     */
+    private void setValue(Properties p, String name, bool value) {
+        Assert.isTrue(p !is null);
+        p.put(name, value is true ? IPreferenceStore.TRUE
+                : IPreferenceStore.FALSE);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/RadioGroupFieldEditor.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,355 @@
+/*******************************************************************************
+ * 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.preference.RadioGroupFieldEditor;
+
+import dwtx.jface.preference.FieldEditor;
+
+import dwt.DWT;
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.events.SelectionAdapter;
+import dwt.events.SelectionEvent;
+import dwt.graphics.Font;
+import dwt.layout.GridData;
+import dwt.layout.GridLayout;
+import dwt.widgets.Button;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Group;
+import dwtx.core.runtime.Assert;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A field editor for an enumeration type preference.
+ * The choices are presented as a list of radio buttons.
+ */
+public class RadioGroupFieldEditor : FieldEditor {
+
+    /**
+     * List of radio button entries of the form [label,value].
+     */
+    private String[][] labelsAndValues;
+
+    /**
+     * Number of columns into which to arrange the radio buttons.
+     */
+    private int numColumns;
+
+    /**
+     * Indent used for the first column of the radion button matrix.
+     */
+    private int indent = HORIZONTAL_GAP;
+
+    /**
+     * The current value, or <code>null</code> if none.
+     */
+    private String value;
+
+    /**
+     * The box of radio buttons, or <code>null</code> if none
+     * (before creation and after disposal).
+     */
+    private Composite radioBox;
+
+    /**
+     * The radio buttons, or <code>null</code> if none
+     * (before creation and after disposal).
+     */
+    private Button[] radioButtons;
+
+    /**
+     * Whether to use a Group control.
+     */
+    private bool useGroup;
+
+    /**
+     * Creates a new radio group field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a radio group field editor.
+     * This constructor does not use a <code>Group</code> to contain the radio buttons.
+     * It is equivalent to using the following constructor with <code>false</code>
+     * for the <code>useGroup</code> argument.
+     * <p>
+     * Example usage:
+     * <pre>
+     *      RadioGroupFieldEditor editor= new RadioGroupFieldEditor(
+     *          "GeneralPage.DoubleClick", resName, 1,
+     *          new String[][] {
+     *              {"Open Browser", "open"},
+     *              {"Expand Tree", "expand"}
+     *          },
+     *          parent);
+     * </pre>
+     * </p>
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param numColumns the number of columns for the radio button presentation
+     * @param labelAndValues list of radio button [label, value] entries;
+     *  the value is returned when the radio button is selected
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText, int numColumns,
+            String[][] labelAndValues, Composite parent) {
+        this(name, labelText, numColumns, labelAndValues, parent, false);
+    }
+
+    /**
+     * Creates a radio group field editor.
+     * <p>
+     * Example usage:
+     * <pre>
+     *      RadioGroupFieldEditor editor= new RadioGroupFieldEditor(
+     *          "GeneralPage.DoubleClick", resName, 1,
+     *          new String[][] {
+     *              {"Open Browser", "open"},
+     *              {"Expand Tree", "expand"}
+     *          },
+     *          parent,
+     *          true);
+     * </pre>
+     * </p>
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param numColumns the number of columns for the radio button presentation
+     * @param labelAndValues list of radio button [label, value] entries;
+     *  the value is returned when the radio button is selected
+     * @param parent the parent of the field editor's control
+     * @param useGroup whether to use a Group control to contain the radio buttons
+     */
+    public this(String name, String labelText, int numColumns,
+            String[][] labelAndValues, Composite parent, bool useGroup) {
+        init(name, labelText);
+        Assert.isTrue(checkArray(labelAndValues));
+        this.labelsAndValues = labelAndValues;
+        this.numColumns = numColumns;
+        this.useGroup = useGroup;
+        createControl(parent);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void adjustForNumColumns(int numColumns) {
+        Control control = getLabelControl();
+        if (control !is null) {
+            (cast(GridData) control.getLayoutData()).horizontalSpan = numColumns;
+        }
+        (cast(GridData) radioBox.getLayoutData()).horizontalSpan = numColumns;
+    }
+
+    /**
+     * Checks whether given <code>String[][]</code> is of "type"
+     * <code>String[][2]</code>.
+     * @param table
+     *
+     * @return <code>true</code> if it is ok, and <code>false</code> otherwise
+     */
+    private bool checkArray(String[][] table) {
+        if (table is null) {
+            return false;
+        }
+        for (int i = 0; i < table.length; i++) {
+            String[] array = table[i];
+            if (array is null || array.length !is 2) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doFillIntoGrid(Composite parent, int numColumns) {
+        if (useGroup) {
+            Control control = getRadioBoxControl(parent);
+            GridData gd = new GridData(GridData.FILL_HORIZONTAL);
+            control.setLayoutData(gd);
+        } else {
+            Control control = getLabelControl(parent);
+            GridData gd = new GridData();
+            gd.horizontalSpan = numColumns;
+            control.setLayoutData(gd);
+            control = getRadioBoxControl(parent);
+            gd = new GridData();
+            gd.horizontalSpan = numColumns;
+            gd.horizontalIndent = indent;
+            control.setLayoutData(gd);
+        }
+
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doLoad() {
+        updateValue(getPreferenceStore().getString(getPreferenceName()));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doLoadDefault() {
+        updateValue(getPreferenceStore().getDefaultString(getPreferenceName()));
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doStore() {
+        if (value is null) {
+            getPreferenceStore().setToDefault(getPreferenceName());
+            return;
+        }
+
+        getPreferenceStore().setValue(getPreferenceName(), value);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public int getNumberOfControls() {
+        return 1;
+    }
+
+    /**
+     * Returns this field editor's radio group control.
+     * @param parent The parent to create the radioBox in
+     * @return the radio group control
+     */
+    public Composite getRadioBoxControl(Composite parent) {
+        if (radioBox is null) {
+
+            Font font = parent.getFont();
+
+            if (useGroup) {
+                Group group = new Group(parent, DWT.NONE);
+                group.setFont(font);
+                String text = getLabelText();
+                if (text !is null) {
+                    group.setText(text);
+                }
+                radioBox = group;
+                GridLayout layout = new GridLayout();
+                layout.horizontalSpacing = HORIZONTAL_GAP;
+                layout.numColumns = numColumns;
+                radioBox.setLayout(layout);
+            } else {
+                radioBox = new Composite(parent, DWT.NONE);
+                GridLayout layout = new GridLayout();
+                layout.marginWidth = 0;
+                layout.marginHeight = 0;
+                layout.horizontalSpacing = HORIZONTAL_GAP;
+                layout.numColumns = numColumns;
+                radioBox.setLayout(layout);
+                radioBox.setFont(font);
+            }
+
+            radioButtons = new Button[labelsAndValues.length];
+            for (int i = 0; i < labelsAndValues.length; i++) {
+                Button radio = new Button(radioBox, DWT.RADIO | DWT.LEFT);
+                radioButtons[i] = radio;
+                String[] labelAndValue = labelsAndValues[i];
+                radio.setText(labelAndValue[0]);
+                radio.setData(stringcast(labelAndValue[1]));
+                radio.setFont(font);
+                radio.addSelectionListener(new class SelectionAdapter {
+                    public void widgetSelected(SelectionEvent event) {
+                        String oldValue = value;
+                        value = stringcast( event.widget.getData() );
+                        setPresentsDefaultValue(false);
+                        fireValueChanged(VALUE, stringcast(oldValue), stringcast(value));
+                    }
+                });
+            }
+            radioBox.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    radioBox = null;
+                    radioButtons = null;
+                }
+            });
+        } else {
+            checkParent(radioBox, parent);
+        }
+        return radioBox;
+    }
+
+    /**
+     * Sets the indent used for the first column of the radion button matrix.
+     *
+     * @param indent the indent (in pixels)
+     */
+    public void setIndent(int indent) {
+        if (indent < 0) {
+            this.indent = 0;
+        } else {
+            this.indent = indent;
+        }
+    }
+
+    /**
+     * Select the radio button that conforms to the given value.
+     *
+     * @param selectedValue the selected value
+     */
+    private void updateValue(String selectedValue) {
+        this.value = selectedValue;
+        if (radioButtons is null) {
+            return;
+        }
+
+        if (this.value !is null) {
+            bool found = false;
+            for (int i = 0; i < radioButtons.length; i++) {
+                Button radio = radioButtons[i];
+                bool selection = false;
+                if (stringcast( radio.getData()).equals(this.value)) {
+                    selection = true;
+                    found = true;
+                }
+                radio.setSelection(selection);
+            }
+            if (found) {
+                return;
+            }
+        }
+
+        // We weren't able to find the value. So we select the first
+        // radio button as a default.
+        if (radioButtons.length > 0) {
+            radioButtons[0].setSelection(true);
+            this.value = stringcast( radioButtons[0].getData());
+        }
+        return;
+    }
+
+    /*
+     * @see FieldEditor.setEnabled(bool,Composite).
+     */
+    public void setEnabled(bool enabled, Composite parent) {
+        if (!useGroup) {
+            super.setEnabled(enabled, parent);
+        }
+        for (int i = 0; i < radioButtons.length; i++) {
+            radioButtons[i].setEnabled(enabled);
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/ScaleFieldEditor.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,386 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.preference.ScaleFieldEditor;
+
+import dwtx.jface.preference.FieldEditor;
+
+import dwt.DWT;
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.events.SelectionAdapter;
+import dwt.events.SelectionEvent;
+import dwt.layout.GridData;
+import dwt.widgets.Composite;
+import dwt.widgets.Control;
+import dwt.widgets.Scale;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A field editor for an integer type preference. This class may be used as is,
+ * or subclassed as required.
+ *
+ * @since 3.0
+ */
+public class ScaleFieldEditor : FieldEditor {
+
+    /**
+     * Value that will feed Scale.setIncrement(int).
+     */
+    private int incrementValue;
+
+    /**
+     * Value that will feed Scale.setMaximum(int).
+     */
+    private int maxValue;
+
+    /**
+     * Value that will feed Scale.setMinimum(int).
+     */
+    private int minValue;
+
+    /**
+     * Old integer value.
+     */
+    private int oldValue;
+
+    /**
+     * Value that will feed Scale.setPageIncrement(int).
+     */
+    private int pageIncrementValue;
+
+    /**
+     * The scale, or <code>null</code> if none.
+     */
+    protected Scale scale;
+
+    /**
+     * Creates a scale field editor.
+     *
+     * @param name
+     *            the name of the preference this field editor works on
+     * @param labelText
+     *            the label text of the field editor
+     * @param parent
+     *            the parent of the field editor's control
+     */
+    public this(String name, String labelText, Composite parent) {
+        super(name, labelText, parent);
+        setDefaultValues();
+    }
+
+    /**
+     * Creates a scale field editor with particular scale values.
+     *
+     * @param name
+     *            the name of the preference this field editor works on
+     * @param labelText
+     *            the label text of the field editor
+     * @param parent
+     *            the parent of the field editor's control
+     * @param min
+     *            the value used for Scale.setMinimum(int).
+     * @param max
+     *            the value used for Scale.setMaximum(int).
+     * @param increment
+     *            the value used for Scale.setIncrement(int).
+     * @param pageIncrement
+     *            the value used for Scale.setPageIncrement(int).
+     */
+    public this(String name, String labelText, Composite parent,
+            int min, int max, int increment, int pageIncrement) {
+        super(name, labelText, parent);
+        setValues(min, max, increment, pageIncrement);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.preference.FieldEditor#adjustForNumColumns(int)
+     */
+    protected void adjustForNumColumns(int numColumns) {
+        (cast(GridData) scale.getLayoutData()).horizontalSpan = numColumns - 1;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.preference.FieldEditor#doFillIntoGrid(dwt.widgets.Composite,
+     *      int)
+     */
+    protected void doFillIntoGrid(Composite parent, int numColumns) {
+        Control control = getLabelControl(parent);
+        GridData gd = new GridData();
+        control.setLayoutData(gd);
+
+        scale = getScaleControl(parent);
+        gd = new GridData(GridData.FILL_HORIZONTAL);
+        gd.verticalAlignment = GridData.FILL;
+        gd.horizontalSpan = numColumns - 1;
+        gd.grabExcessHorizontalSpace = true;
+        scale.setLayoutData(gd);
+        updateScale();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.preference.FieldEditor#doLoad()
+     */
+    protected void doLoad() {
+        if (scale !is null) {
+            int value = getPreferenceStore().getInt(getPreferenceName());
+            scale.setSelection(value);
+            oldValue = value;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.preference.FieldEditor#doLoadDefault()
+     */
+    protected void doLoadDefault() {
+        if (scale !is null) {
+            int value = getPreferenceStore().getDefaultInt(getPreferenceName());
+            scale.setSelection(value);
+        }
+        valueChanged();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.preference.FieldEditor#doStore()
+     */
+    protected void doStore() {
+        getPreferenceStore()
+                .setValue(getPreferenceName(), scale.getSelection());
+    }
+
+    /**
+     * Returns the value that will be used for Scale.setIncrement(int).
+     *
+     * @return the value.
+     * @see dwt.widgets.Scale#setIncrement(int)
+     */
+    public int getIncrement() {
+        return incrementValue;
+    }
+
+    /**
+     * Returns the value that will be used for Scale.setMaximum(int).
+     *
+     * @return the value.
+     * @see dwt.widgets.Scale#setMaximum(int)
+     */
+    public int getMaximum() {
+        return maxValue;
+    }
+
+    /**
+     * Returns the value that will be used for Scale.setMinimum(int).
+     *
+     * @return the value.
+     * @see dwt.widgets.Scale#setMinimum(int)
+     */
+    public int getMinimum() {
+        return minValue;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.preference.FieldEditor#getNumberOfControls()
+     */
+    public int getNumberOfControls() {
+        return 2;
+    }
+
+    /**
+     * Returns the value that will be used for Scale.setPageIncrement(int).
+     *
+     * @return the value.
+     * @see dwt.widgets.Scale#setPageIncrement(int)
+     */
+    public int getPageIncrement() {
+        return pageIncrementValue;
+    }
+
+    /**
+     * Returns this field editor's scale control.
+     *
+     * @return the scale control, or <code>null</code> if no scale field is
+     *         created yet
+     */
+    public Scale getScaleControl() {
+        return scale;
+    }
+
+    /**
+     * Returns this field editor's scale control. The control is created if it
+     * does not yet exist.
+     *
+     * @param parent
+     *            the parent
+     * @return the scale control
+     */
+    private Scale getScaleControl(Composite parent) {
+        if (scale is null) {
+            scale = new Scale(parent, DWT.HORIZONTAL);
+            scale.setFont(parent.getFont());
+            scale.addSelectionListener(new class SelectionAdapter {
+                public void widgetSelected(SelectionEvent event) {
+                    valueChanged();
+                }
+            });
+            scale.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    scale = null;
+                }
+            });
+        } else {
+            checkParent(scale, parent);
+        }
+        return scale;
+    }
+
+    /**
+     * Set default values for the various scale fields.  These defaults are:<br>
+     * <ul>
+     * <li>Minimum  = 0
+     * <li>Maximim = 10
+     * <li>Increment = 1
+     * <li>Page Increment = 1
+     * </ul>
+     */
+    private void setDefaultValues() {
+        setValues(0, 10, 1, 1);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.preference.FieldEditor#setFocus()
+     */
+    public void setFocus() {
+        if (scale !is null && !scale.isDisposed()) {
+            scale.setFocus();
+        }
+    }
+
+    /**
+     * Set the value to be used for Scale.setIncrement(int) and update the
+     * scale.
+     *
+     * @param increment
+     *            a value greater than 0.
+     * @see dwt.widgets.Scale#setIncrement(int)
+     */
+    public void setIncrement(int increment) {
+        this.incrementValue = increment;
+        updateScale();
+    }
+
+    /**
+     * Set the value to be used for Scale.setMaximum(int) and update the
+     * scale.
+     *
+     * @param max
+     *            a value greater than 0.
+     * @see dwt.widgets.Scale#setMaximum(int)
+     */
+    public void setMaximum(int max) {
+        this.maxValue = max;
+        updateScale();
+    }
+
+    /**
+     * Set the value to be used for Scale.setMinumum(int) and update the
+     * scale.
+     *
+     * @param min
+     *            a value greater than 0.
+     * @see dwt.widgets.Scale#setMinimum(int)
+     */
+    public void setMinimum(int min) {
+        this.minValue = min;
+        updateScale();
+    }
+
+    /**
+     * Set the value to be used for Scale.setPageIncrement(int) and update the
+     * scale.
+     *
+     * @param pageIncrement
+     *            a value greater than 0.
+     * @see dwt.widgets.Scale#setPageIncrement(int)
+     */
+    public void setPageIncrement(int pageIncrement) {
+        this.pageIncrementValue = pageIncrement;
+        updateScale();
+    }
+
+    /**
+     * Set all Scale values.
+     *
+     * @param min
+     *            the value used for Scale.setMinimum(int).
+     * @param max
+     *            the value used for Scale.setMaximum(int).
+     * @param increment
+     *            the value used for Scale.setIncrement(int).
+     * @param pageIncrement
+     *            the value used for Scale.setPageIncrement(int).
+     */
+    private void setValues(int min, int max, int increment, int pageIncrement) {
+        this.incrementValue = increment;
+        this.maxValue = max;
+        this.minValue = min;
+        this.pageIncrementValue = pageIncrement;
+        updateScale();
+    }
+
+    /**
+     * Update the scale particulars with set values.
+     */
+    private void updateScale() {
+        if (scale !is null && !scale.isDisposed()) {
+            scale.setMinimum(getMinimum());
+            scale.setMaximum(getMaximum());
+            scale.setIncrement(getIncrement());
+            scale.setPageIncrement(getPageIncrement());
+        }
+    }
+
+    /**
+     * Informs this field editor's listener, if it has one, about a change to
+     * the value (<code>VALUE</code> property) provided that the old and new
+     * values are different.
+     * <p>
+     * This hook is <em>not</em> called when the scale is initialized (or
+     * reset to the default value) from the preference store.
+     * </p>
+     */
+    protected void valueChanged() {
+        setPresentsDefaultValue(false);
+
+        int newValue = scale.getSelection();
+        if (newValue !is oldValue) {
+            fireStateChanged(IS_VALID, false, true);
+            fireValueChanged(VALUE, new ValueWrapperInt(oldValue),
+                    new ValueWrapperInt(newValue));
+            oldValue = newValue;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/StringButtonFieldEditor.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Thierry Lach - thierry.lach@bbdodetroit.com - Fix for Bug 37155
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.preference.StringButtonFieldEditor;
+
+import dwtx.jface.preference.StringFieldEditor;
+
+import dwt.DWT;
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.events.SelectionAdapter;
+import dwt.events.SelectionEvent;
+import dwt.graphics.Point;
+import dwt.layout.GridData;
+import dwt.widgets.Button;
+import dwt.widgets.Composite;
+import dwt.widgets.Shell;
+import dwtx.core.runtime.Assert;
+import dwtx.jface.dialogs.IDialogConstants;
+import dwtx.jface.resource.JFaceResources;
+
+import dwt.dwthelper.utils;
+
+/**
+ * An abstract field editor for a string type preference that presents
+ * a string input field with a change button to its right to edit the
+ * input field's content. When the user presses the change button, the
+ * abstract framework method <code>changePressed()</code> gets called
+ * to compute a new string.
+ */
+public abstract class StringButtonFieldEditor : StringFieldEditor {
+
+    /**
+     * The change button, or <code>null</code> if none
+     * (before creation and after disposal).
+     */
+    private Button changeButton;
+
+    /**
+     * The text for the change button, or <code>null</code> if missing.
+     */
+    private String changeButtonText;
+
+    /**
+     * Creates a new string button field editor
+     */
+    protected this() {
+    }
+
+    /**
+     * Creates a string button field editor.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    protected this(String name, String labelText,
+            Composite parent) {
+        init(name, labelText);
+        createControl(parent);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void adjustForNumColumns(int numColumns) {
+        (cast(GridData) getTextControl().getLayoutData()).horizontalSpan = numColumns - 2;
+    }
+
+    /**
+     * Notifies that this field editor's change button has been pressed.
+     * <p>
+     * Subclasses must implement this method to provide a corresponding
+     * new string for the text field. If the returned value is <code>null</code>,
+     * the currently displayed value remains.
+     * </p>
+     *
+     * @return the new string to display, or <code>null</code> to leave the
+     *  old string showing
+     */
+    protected abstract String changePressed();
+
+    /* (non-Javadoc)
+     * Method declared on StringFieldEditor (and FieldEditor).
+     */
+    protected void doFillIntoGrid(Composite parent, int numColumns) {
+        super.doFillIntoGrid(parent, numColumns - 1);
+        changeButton = getChangeControl(parent);
+        GridData gd = new GridData();
+        gd.horizontalAlignment = GridData.FILL;
+        int widthHint = convertHorizontalDLUsToPixels(changeButton,
+                IDialogConstants.BUTTON_WIDTH);
+        gd.widthHint = Math.max(widthHint, changeButton.computeSize(
+                DWT.DEFAULT, DWT.DEFAULT, true).x);
+        changeButton.setLayoutData(gd);
+    }
+
+    /**
+     * Get the change control. Create it in parent if required.
+     * @param parent
+     * @return Button
+     */
+    protected Button getChangeControl(Composite parent) {
+        if (changeButton is null) {
+            changeButton = new Button(parent, DWT.PUSH);
+            if (changeButtonText is null) {
+                changeButtonText = JFaceResources.getString("openChange"); //$NON-NLS-1$
+            }
+            changeButton.setText(changeButtonText);
+            changeButton.setFont(parent.getFont());
+            changeButton.addSelectionListener(new class SelectionAdapter {
+                public void widgetSelected(SelectionEvent evt) {
+                    String newValue = changePressed();
+                    if (newValue !is null) {
+                        setStringValue(newValue);
+                    }
+                }
+            });
+            changeButton.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    changeButton = null;
+                }
+            });
+        } else {
+            checkParent(changeButton, parent);
+        }
+        return changeButton;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public int getNumberOfControls() {
+        return 3;
+    }
+
+    /**
+     * Returns this field editor's shell.
+     *
+     * @return the shell
+     */
+    protected Shell getShell() {
+        if (changeButton is null) {
+            return null;
+        }
+        return changeButton.getShell();
+    }
+
+    /**
+     * Sets the text of the change button.
+     *
+     * @param text the new text
+     */
+    public void setChangeButtonText(String text) {
+        Assert.isNotNull(text);
+        changeButtonText = text;
+        if (changeButton !is null) {
+            changeButton.setText(text);
+            Point prefSize = changeButton.computeSize(DWT.DEFAULT, DWT.DEFAULT);
+            GridData data = cast(GridData)changeButton.getLayoutData();
+            data.widthHint = Math.max(DWT.DEFAULT, prefSize.x);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see dwtx.jface.preference.FieldEditor#setEnabled(bool, dwt.widgets.Composite)
+     */
+    public void setEnabled(bool enabled, Composite parent) {
+        super.setEnabled(enabled, parent);
+        if (changeButton !is null) {
+            changeButton.setEnabled(enabled);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/preference/StringFieldEditor.d	Sat Apr 05 01:45:47 2008 +0200
@@ -0,0 +1,532 @@
+/*******************************************************************************
+ * 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.preference.StringFieldEditor;
+
+import dwtx.jface.preference.FieldEditor;
+
+import dwt.DWT;
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.events.FocusAdapter;
+import dwt.events.FocusEvent;
+import dwt.events.KeyAdapter;
+import dwt.events.KeyEvent;
+import dwt.graphics.GC;
+import dwt.graphics.Point;
+import dwt.layout.GridData;
+import dwt.widgets.Composite;
+import dwt.widgets.Text;
+import dwtx.core.runtime.Assert;
+import dwtx.jface.resource.JFaceResources;
+
+import dwt.dwthelper.utils;
+
+/**
+ * A field editor for a string type preference.
+ * <p>
+ * This class may be used as is, or subclassed as required.
+ * </p>
+ */
+public class StringFieldEditor : FieldEditor {
+
+    alias FieldEditor.showErrorMessage showErrorMessage;
+    /**
+     * Validation strategy constant (value <code>0</code>) indicating that
+     * the editor should perform validation after every key stroke.
+     *
+     * @see #setValidateStrategy
+     */
+    public static const int VALIDATE_ON_KEY_STROKE = 0;
+
+    /**
+     * Validation strategy constant (value <code>1</code>) indicating that
+     * the editor should perform validation only when the text widget
+     * loses focus.
+     *
+     * @see #setValidateStrategy
+     */
+    public static const int VALIDATE_ON_FOCUS_LOST = 1;
+
+    /**
+     * Text limit constant (value <code>-1</code>) indicating unlimited
+     * text limit and width.
+     */
+    public static int UNLIMITED = -1;
+
+    /**
+     * Cached valid state.
+     */
+    private bool isValid_;
+
+    /**
+     * Old text value.
+     */
+    private String oldValue;
+
+    /**
+     * The text field, or <code>null</code> if none.
+     */
+    Text textField;
+
+    /**
+     * Width of text field in characters; initially unlimited.
+     */
+    private int widthInChars;
+
+    /**
+     * Text limit of text field in characters; initially unlimited.
+     */
+    private int textLimit;
+
+    /**
+     * The error message, or <code>null</code> if none.
+     */
+    private String errorMessage;
+
+    /**
+     * Indicates whether the empty string is legal;
+     * <code>true</code> by default.
+     */
+    private bool emptyStringAllowed = true;
+
+    /**
+     * The validation strategy;
+     * <code>VALIDATE_ON_KEY_STROKE</code> by default.
+     */
+    private int validateStrategy;
+
+    /**
+     * Creates a new string field editor
+     */
+    protected this() {
+        widthInChars = UNLIMITED;
+        textLimit = UNLIMITED;
+        validateStrategy = VALIDATE_ON_KEY_STROKE;
+    }
+
+    /**
+     * Creates a string field editor.
+     * Use the method <code>setTextLimit</code> to limit the text.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param width the width of the text input field in characters,
+     *  or <code>UNLIMITED</code> for no limit
+     * @param strategy either <code>VALIDATE_ON_KEY_STROKE</code> to perform
+     *  on the fly checking (the default), or <code>VALIDATE_ON_FOCUS_LOST</code> to
+     *  perform validation only after the text has been typed in
+     * @param parent the parent of the field editor's control
+     * @since 2.0
+     */
+    public this(String name, String labelText, int width,
+            int strategy, Composite parent) {
+        this();
+        init(name, labelText);
+        widthInChars = width;
+        setValidateStrategy(strategy);
+        isValid_ = false;
+        errorMessage = JFaceResources
+                .getString("StringFieldEditor.errorMessage");//$NON-NLS-1$
+        createControl(parent);
+    }
+
+    /**
+     * Creates a string field editor.
+     * Use the method <code>setTextLimit</code> to limit the text.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param width the width of the text input field in characters,
+     *  or <code>UNLIMITED</code> for no limit
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText, int width,
+            Composite parent) {
+        this(name, labelText, width, VALIDATE_ON_KEY_STROKE, parent);
+    }
+
+    /**
+     * Creates a string field editor of unlimited width.
+     * Use the method <code>setTextLimit</code> to limit the text.
+     *
+     * @param name the name of the preference this field editor works on
+     * @param labelText the label text of the field editor
+     * @param parent the parent of the field editor's control
+     */
+    public this(String name, String labelText, Composite parent) {
+        this(name, labelText, UNLIMITED, parent);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void adjustForNumColumns(int numColumns) {
+        GridData gd = cast(GridData) textField.getLayoutData();
+        gd.horizontalSpan = numColumns - 1;
+        // We only grab excess space if we have to
+        // If another field editor has more columns then
+        // we assume it is setting the width.
+        gd.grabExcessHorizontalSpace = gd.horizontalSpan is 1;
+    }
+
+    /**
+     * Checks whether the text input field contains a valid value or not.
+     *
+     * @return <code>true</code> if the field value is valid,
+     *   and <code>false</code> if invalid
+     */
+    protected bool checkState() {
+        bool result = false;
+        if (emptyStringAllowed) {
+            result = true;
+        }
+
+        if (textField is null) {
+            result = false;
+        }
+
+        String txt = textField.getText();
+
+        result = (txt.trim().length > 0) || emptyStringAllowed;
+
+        // call hook for subclasses
+        result = result && doCheckState();
+
+        if (result) {
+            clearErrorMessage();
+        } else {
+            showErrorMessage(errorMessage);
+        }
+
+        return result;
+    }
+
+    /**
+     * Hook for subclasses to do specific state checks.
+     * <p>
+     * The default implementation of this framework method does
+     * nothing and returns <code>true</code>.  Subclasses should
+     * override this method to specific state checks.
+     * </p>
+     *
+     * @return <code>true</code> if the field value is valid,
+     *   and <code>false</code> if invalid
+     */
+    protected bool doCheckState() {
+        return true;
+    }
+
+    /**
+     * Fills this field editor's basic controls into the given parent.
+     * <p>
+     * The string field implementation of this <code>FieldEditor</code>
+     * framework method contributes the text field. Subclasses may override
+     * but must call <code>super.doFillIntoGrid</code>.
+     * </p>
+     */
+    protected void doFillIntoGrid(Composite parent, int numColumns) {
+        getLabelControl(parent);
+
+        textField = getTextControl(parent);
+        GridData gd = new GridData();
+        gd.horizontalSpan = numColumns - 1;
+        if (widthInChars !is UNLIMITED) {
+            GC gc = new GC(textField);
+            try {
+                Point extent = gc.textExtent("X");//$NON-NLS-1$
+                gd.widthHint = widthInChars * extent.x;
+            } finally {
+                gc.dispose();
+            }
+        } else {
+            gd.horizontalAlignment = GridData.FILL;
+            gd.grabExcessHorizontalSpace = true;
+        }
+        textField.setLayoutData(gd);
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doLoad() {
+        if (textField !is null) {
+            String value = getPreferenceStore().getString(getPreferenceName());
+            textField.setText(value);
+            oldValue = value;
+        }
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doLoadDefault() {
+        if (textField !is null) {
+            String value = getPreferenceStore().getDefaultString(
+                    getPreferenceName());
+            textField.setText(value);
+        }
+        valueChanged();
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void doStore() {
+        getPreferenceStore().setValue(getPreferenceName(), textField.getText());
+    }
+
+    /**
+     * Returns the error message that will be displayed when and if
+     * an error occurs.
+     *
+     * @return the error message, or <code>null</code> if none
+     */
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public int getNumberOfControls() {
+        return 2;
+    }
+
+    /**
+     * Returns the field editor's value.
+     *
+     * @return the current value
+     */
+    public String getStringValue() {
+        if (textField !is null) {
+            return textField.getText();
+        }
+
+        return getPreferenceStore().getString(getPreferenceName());
+    }
+
+    /**
+     * Returns this field editor's text control.
+     *
+     * @return the text control, or <code>null</code> if no
+     * text field is created yet
+     */
+    protected Text getTextControl() {
+        return textField;
+    }
+
+    /**
+     * Returns this field editor's text control.
+     * <p>
+     * The control is created if it does not yet exist
+     * </p>
+     *
+     * @param parent the parent
+     * @return the text control
+     */
+    public Text getTextControl(Composite parent) {
+        if (textField is null) {
+            textField = new Text(parent, DWT.SINGLE | DWT.BORDER);
+            textField.setFont(parent.getFont());
+            switch (validateStrategy) {
+            case VALIDATE_ON_KEY_STROKE:
+                textField.addKeyListener(new class KeyAdapter {
+
+                    /* (non-Javadoc)
+                     * @see dwt.events.KeyAdapter#keyReleased(dwt.events.KeyEvent)
+                     */
+                    public void keyReleased(KeyEvent e) {
+                        valueChanged();
+                    }
+                });
+
+                break;
+            case VALIDATE_ON_FOCUS_LOST:
+                textField.addKeyListener(new class KeyAdapter {
+                    public void keyPressed(KeyEvent e) {
+                        clearErrorMessage();
+                    }
+                });
+                textField.addFocusListener(new class FocusAdapter {
+                    public void focusGained(FocusEvent e) {
+                        refreshValidState();
+                    }
+
+                    public void focusLost(FocusEvent e) {
+                        valueChanged();
+                        clearErrorMessage();
+                    }
+                });
+                break;
+            default:
+                Assert.isTrue(false, "Unknown validate strategy");//$NON-NLS-1$
+            }
+            textField.addDisposeListener(new class DisposeListener {
+                public void widgetDisposed(DisposeEvent event) {
+                    textField = null;
+                }
+            });
+            if (textLimit > 0) {//Only set limits above 0 - see DWT spec
+                textField.setTextLimit(textLimit);
+            }
+        } else {
+            checkParent(textField, parent);
+        }
+        return textField;
+    }
+
+    /**
+     * Returns whether an empty string is a valid value.
+     *
+     * @return <code>true</code> if an empty string is a valid value, and
+     *  <code>false</code> if an empty string is invalid
+     * @see #setEmptyStringAllowed
+     */
+    public bool isEmptyStringAllowed() {
+        return emptyStringAllowed;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public bool isValid() {
+        return isValid_;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    protected void refreshValidState() {
+        isValid_ = checkState();
+    }
+
+    /**
+     * Sets whether the empty string is a valid value or not.
+     *
+     * @param b <code>true</code> if the empty string is allowed,
+     *  and <code>false</code> if it is considered invalid
+     */
+    public void setEmptyStringAllowed(bool b) {
+        emptyStringAllowed = b;
+    }
+
+    /**
+     * Sets the error message that will be displayed when and if
+     * an error occurs.
+     *
+     * @param message the error message
+     */
+    public void setErrorMessage(String message) {
+        errorMessage = message;
+    }
+
+    /* (non-Javadoc)
+     * Method declared on FieldEditor.
+     */
+    public void setFocus() {
+        if (textField !is null) {
+            textField.setFocus();
+        }
+    }
+
+    /**
+     * Sets this field editor's value.
+     *
+     * @param value the new value, or <code>null</code> meaning the empty string
+     */
+    public void setStringValue(String value) {
+        if (textField !is null) {
+            if (value is null) {
+                value = "";//$NON-NLS-1$
+            }
+            oldValue = textField.getText();
+            if (!oldValue.equals(value)) {
+                textField.setText(value);
+                valueChanged();
+            }
+        }
+    }
+
+    /**
+     * Sets this text field's text limit.
+     *
+     * @param limit the limit on the number of character in the text
+     *  input field, or <code>UNLIMITED</code> for no limit
+
+     */
+    public void setTextLimit(int limit) {
+        textLimit = limit;
+        if (textField !is null) {
+            textField.setTextLimit(limit);
+        }
+    }
+
+    /**
+     * Sets the strategy for validating the text.
+     * <p>
+     * Calling this method has no effect after <code>createPartControl</code>
+     * is called. Thus this method is really only useful for subclasses to call
+     * in their constructor. However, it has public visibility for backward
+     * compatibility.
+     * </p>
+     *
+     * @param value either <code>VALIDATE_ON_KEY_STROKE</code> to perform
+     *  on the fly checking (the default), or <code>VALIDATE_ON_FOCUS_LOST</code> to
+     *  perform validation only after the text has been typed in
+     */
+    public void setValidateStrategy(int value) {
+        Assert.isTrue(value is VALIDATE_ON_FOCUS_LOST
+                || value is VALIDATE_ON_KEY_STROKE);
+        validateStrategy = value;
+    }
+
+    /**
+     * Shows the error message set via <code>setErrorMessage</code>.
+     */
+    public void showErrorMessage() {
+        showErrorMessage(errorMessage);
+    }
+
+    /**
+     * Informs this field editor's listener, if it has one, about a change
+     * to the value (<code>VALUE</code> property) provided that the old and
+     * new values are different.
+     * <p>
+     * This hook is <em>not</em> called when the text is initialized
+     * (or reset to the default value) from the preference store.
+     * </p>
+     */
+    protected void valueChanged() {
+        setPresentsDefaultValue(false);
+        bool oldState = isValid_;
+        refreshValidState();
+
+        if (isValid_ !is oldState) {
+            fireStateChanged(IS_VALID, oldState, isValid_);
+        }
+
+        String newValue = textField.getText();
+        if (!newValue.equals(oldValue)) {
+            fireValueChanged(VALUE, stringcast(oldValue), stringcast(newValue));
+            oldValue = newValue;
+        }
+    }
+
+    /*
+     * @see FieldEditor.setEnabled(bool,Composite).
+     */
+    public void setEnabled(bool enabled, Composite parent) {
+        super.setEnabled(enabled, parent);
+        getTextControl(parent).setEnabled(enabled);
+    }
+}