org.eclipse.birt.report.designer.ui.preferences.OptionsConfigurationBlock.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.birt.report.designer.ui.preferences.OptionsConfigurationBlock.java

Source

/*******************************************************************************
 * Copyright (c) 2004 Actuate Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Actuate Corporation  - initial API and implementation
 *******************************************************************************/

package org.eclipse.birt.report.designer.ui.preferences;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.birt.core.preference.IPreferences;
import org.eclipse.birt.report.designer.ui.ReportPlugin;
import org.eclipse.birt.report.designer.ui.util.PixelConverter;
import org.eclipse.core.resources.IProject;
import org.eclipse.jface.dialogs.ControlEnableState;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.plugin.AbstractUIPlugin;

/**
 * Abstract options configuration block providing a general implementation for
 * setting up an options configuration page.
 * 
 * @since 2.1
 */
public abstract class OptionsConfigurationBlock {

    public static final class Key {

        private String fQualifier;
        private String fKey;
        private String value;

        public Key(String qualifier, String key) {
            fQualifier = qualifier;
            fKey = key;
        }

        public String getName() {
            return fKey;
        }

        public void apply(IPreferences preference) {
            if (value != null)
                preference.setValue(fKey, value);
        }

        public String getStoredValue(IPreferences preference) {
            if (value == null)
                value = preference.getString(fKey);
            return value;
        }

        public String getDefaultValue(IPreferences preference) {
            return preference.getDefaultString(fKey);
        }

        public void setStoredValue(IPreferences preference, String value) {
            if (value != null) {
                this.value = value;
            }
        }

        public void setToDefault(IPreferences preference) {
            preference.setToDefault(fKey);
            this.value = preference.getString(fKey);
        }

        public String toString() {
            return fQualifier + '/' + fKey;
        }

        public String getQualifier() {
            return fQualifier;
        }

        public void clear() {
            value = null;
        }

    }

    protected static class ControlData {

        private Key fKey;
        private String[] fValues;

        public ControlData(Key key, String[] values) {
            fKey = key;
            fValues = values;
        }

        public Key getKey() {
            return fKey;
        }

        public String getValue(boolean selection) {
            int index = selection ? 0 : 1;
            return fValues[index];
        }

        public String getValue(int index) {
            return fValues[index];
        }

        public int getSelection(String value) {
            if (value != null) {
                for (int i = 0; i < fValues.length; i++) {
                    if (value.equals(fValues[i])) {
                        return i;
                    }
                }
            }
            return fValues.length - 1; // assume the last option is the least
            // severe
        }
    }

    protected final ArrayList fCheckBoxes;
    protected final ArrayList fComboBoxes;
    protected final ArrayList fTextBoxes;
    protected final ArrayList fRadioButtons;
    protected final HashMap fLabels;
    protected final ArrayList fExpandedComposites;

    private SelectionListener fSelectionListener;
    private ModifyListener fTextModifyListener;

    protected IStatusChangeListener fContext;
    protected IProject fProject; // project or null
    protected Key[] fAllKeys;
    protected Key[] ignoreKeys;
    protected AbstractUIPlugin fPlugin;

    protected IPreferences fPref;

    private Shell fShell;

    private Map fDisabledProjectSettings; // null when project specific

    public OptionsConfigurationBlock(IStatusChangeListener context, AbstractUIPlugin plugin, IProject project) {
        fContext = context;
        fProject = project;

        fPlugin = plugin;
        fPref = PreferenceFactory.getInstance().getPreferences(fPlugin, fProject);

        fCheckBoxes = new ArrayList();
        fComboBoxes = new ArrayList();
        fTextBoxes = new ArrayList(2);
        fRadioButtons = new ArrayList();
        fLabels = new HashMap();
        fExpandedComposites = new ArrayList();
    }

    public void setKeys(Key[] keys) {
        fAllKeys = keys;
        if (fProject == null || hasProjectSpecificOptions(fProject)) {
            fDisabledProjectSettings = null;
        } else {
            /*
             * set default show value
             */
            fDisabledProjectSettings = new IdentityHashMap();
            for (int i = 0; i < keys.length; i++) {
                Key curr = keys[i];
                fDisabledProjectSettings.put(curr, curr.getStoredValue(fPref));
            }
        }
        settingsUpdated();
    }

    public void setIngoreProjectSettingKeys(Key[] keys) {
        ignoreKeys = keys;
    }

    protected void settingsUpdated() {
        // TODO Auto-generated method stub

    }

    protected Key getKey(String plugin, String key) {
        return new Key(plugin, key);
    }

    protected final Key getReportKey(String key) {
        return getKey(ReportPlugin.REPORT_UI, key);
    }

    public void selectOption(String key, String qualifier) {
        for (int i = 0; i < fAllKeys.length; i++) {
            Key curr = fAllKeys[i];
            if (curr.getName().equals(key) && curr.getQualifier().equals(qualifier)) {
                selectOption(curr);
            }
        }
    }

    public void selectOption(Key key) {
        Control control = findControl(key);
        if (control != null) {
            control.setFocus();
        }
    }

    protected Shell getShell() {
        return fShell;
    }

    protected void setShell(Shell shell) {
        fShell = shell;
    }

    protected abstract Control createContents(Composite parent);

    public static class RadioComposite extends Group {

        List<Button> radioBtns = new ArrayList<Button>();;

        public RadioComposite(Composite parent, int style) {
            super(parent, style);
        }

        protected void checkSubclass() {
            /* Do nothing to make sure subclassing is allowed */
        }

        private void IniRadioButtons() {
            if (radioBtns.size() > 0) {
                return;
            }
            Control[] controls = this.getChildren();
            for (int i = 0; i < controls.length; i++) {
                if (controls[i] != null && controls[i] instanceof Button) {
                    radioBtns.add((Button) controls[i]);
                }
            }
        }

        protected int getBtnIndex(Button btn) {
            int index = radioBtns.indexOf(btn);
            return index;
        }

        protected void setSelection(int index) {
            IniRadioButtons();
            if (radioBtns.size() <= 0)
                return;
            if (index < 0 || index >= radioBtns.size()) {
                index = 0;
            }

            for (int i = 0; i < radioBtns.size(); i++) {
                Button btn = radioBtns.get(i);
                if (i == index) {
                    btn.setSelection(true);
                } else {
                    btn.setSelection(false);
                }
            }
        }

        public void addSelectionListener(SelectionListener selectionListener) {
            IniRadioButtons();
            if (radioBtns.size() <= 0)
                return;
            for (int i = 0; i < radioBtns.size(); i++) {
                radioBtns.get(i).addSelectionListener(selectionListener);
            }

        }
    }

    protected RadioComposite addRadioButton(Composite parent, String[] labels, Key key, String[] values,
            int indent) {
        ControlData data = new ControlData(key, values);

        GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
        gd.horizontalSpan = 3;
        gd.horizontalIndent = indent;

        RadioComposite composite = new RadioComposite(parent, SWT.NONE);
        composite.setLayoutData(gd);
        GridLayout layout = new GridLayout(3, false);
        composite.setLayout(layout);
        composite.setText(labels[0]);

        for (int i = 1; i < labels.length; i++) {
            Button radioBtn = new Button(composite, SWT.RADIO);
            radioBtn.setFont(JFaceResources.getDialogFont());
            radioBtn.setLayoutData(new GridData());
            radioBtn.setText(labels[i]);
        }

        composite.setData(data);
        composite.addSelectionListener(getSelectionListener());
        String currValue = getValue(key);
        composite.setSelection(data.getSelection(currValue));
        fRadioButtons.add(composite);

        updateRadioComposite(composite);

        return composite;
    }

    protected Button addCheckBox(Composite parent, String label, Key key, String[] values, int indent) {
        ControlData data = new ControlData(key, values);

        GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
        gd.horizontalSpan = 3;
        gd.horizontalIndent = indent;

        Button checkBox = new Button(parent, SWT.CHECK);
        checkBox.setFont(JFaceResources.getDialogFont());
        checkBox.setText(label);
        checkBox.setData(data);
        checkBox.setLayoutData(gd);
        checkBox.addSelectionListener(getSelectionListener());

        String currValue = getValue(key);
        checkBox.setSelection(data.getSelection(currValue) == 0);

        fCheckBoxes.add(checkBox);

        updateCheckBox(checkBox);

        return checkBox;
    }

    protected Button addCheckBoxWithLink(Composite parent, String label, Key key, String[] values, int indent,
            int widthHint, SelectionListener listener) {
        ControlData data = new ControlData(key, values);

        GridData gd = new GridData(GridData.FILL, GridData.FILL, true, false);
        gd.horizontalSpan = 3;
        gd.horizontalIndent = indent;

        Composite composite = new Composite(parent, SWT.NONE);
        GridLayout layout = new GridLayout();
        layout.marginHeight = 0;
        layout.marginWidth = 0;
        layout.numColumns = 2;
        composite.setLayout(layout);
        composite.setLayoutData(gd);

        Button checkBox = new Button(composite, SWT.CHECK);
        checkBox.setFont(JFaceResources.getDialogFont());
        checkBox.setData(data);
        checkBox.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, false, false));
        checkBox.addSelectionListener(getSelectionListener());

        gd = new GridData(GridData.FILL, GridData.CENTER, true, false);
        gd.widthHint = widthHint;

        Link link = new Link(composite, SWT.NONE);
        link.setText(label);
        link.setLayoutData(gd);
        if (listener != null) {
            link.addSelectionListener(listener);
        }

        String currValue = getValue(key);
        checkBox.setSelection(data.getSelection(currValue) == 0);

        fCheckBoxes.add(checkBox);

        updateCheckBox(checkBox);

        return checkBox;
    }

    protected Combo addComboBox(Composite parent, String label, Key key, String[] values, String[] valueLabels,
            int indent) {
        GridData gd = new GridData();
        gd.horizontalIndent = indent;

        Label labelControl = new Label(parent, SWT.NONE);
        labelControl.setFont(JFaceResources.getDialogFont());
        labelControl.setText(label);
        labelControl.setLayoutData(gd);

        Combo comboBox = newComboControl(parent, key, values, valueLabels);
        gd = new GridData();
        gd.widthHint = 200;
        comboBox.setLayoutData(gd);

        fLabels.put(comboBox, labelControl);

        updateCombo(comboBox);

        return comboBox;
    }

    protected Combo addInversedComboBox(Composite parent, String label, Key key, String[] values,
            String[] valueLabels, int indent) {
        GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
        gd.horizontalIndent = indent;
        gd.horizontalSpan = 3;

        Composite composite = new Composite(parent, SWT.NONE);
        GridLayout layout = new GridLayout();
        layout.marginHeight = 0;
        layout.marginWidth = 0;
        layout.numColumns = 2;
        composite.setLayout(layout);
        composite.setLayoutData(gd);

        Combo comboBox = newComboControl(composite, key, values, valueLabels);
        comboBox.setFont(JFaceResources.getDialogFont());
        comboBox.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));

        Label labelControl = new Label(composite, SWT.LEFT | SWT.WRAP);
        labelControl.setText(label);
        labelControl.setLayoutData(new GridData());

        fLabels.put(comboBox, labelControl);

        updateCombo(comboBox);

        return comboBox;
    }

    protected Combo newComboControl(Composite composite, Key key, String[] values, String[] valueLabels) {
        ControlData data = new ControlData(key, values);

        Combo comboBox = new Combo(composite, SWT.READ_ONLY);
        comboBox.setVisibleItemCount(30);
        comboBox.setItems(valueLabels);
        comboBox.setData(data);
        comboBox.addSelectionListener(getSelectionListener());
        comboBox.setFont(JFaceResources.getDialogFont());

        String currValue = getValue(key);
        comboBox.select(data.getSelection(currValue));

        fComboBoxes.add(comboBox);

        return comboBox;
    }

    protected Text addTextField(Composite parent, String label, Key key, int indent, int widthHint) {
        return addTextField(parent, label, key, indent, widthHint, SWT.BORDER | SWT.SINGLE);
    }

    protected Text addTextField(Composite parent, String label, Key key, int indent, int widthHint, int style) {
        Label labelControl = null;
        if (label != null && label.length() > 0) {
            labelControl = new Label(parent, SWT.WRAP);
            labelControl.setText(label);
            labelControl.setFont(JFaceResources.getDialogFont());
            labelControl.setLayoutData(new GridData());
        }
        Text textBox = new Text(parent, style);
        textBox.setData(key);
        textBox.setLayoutData(new GridData());

        if (labelControl != null)
            fLabels.put(textBox, labelControl);

        String currValue = getValue(key);
        if (currValue != null) {
            textBox.setText(currValue);
        }
        textBox.addModifyListener(getTextModifyListener());

        GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
        if (widthHint != 0) {
            data.widthHint = widthHint;
        } else
            data.grabExcessHorizontalSpace = true;
        data.horizontalIndent = indent;
        if (labelControl != null)
            data.horizontalSpan = 2;
        else
            data.horizontalSpan = 3;
        textBox.setLayoutData(data);

        fTextBoxes.add(textBox);

        updateText(textBox);

        return textBox;
    }

    protected SelectionListener getSelectionListener() {
        if (fSelectionListener == null) {
            fSelectionListener = new SelectionListener() {

                public void widgetDefaultSelected(SelectionEvent e) {
                }

                public void widgetSelected(SelectionEvent e) {
                    controlChanged(e.widget);
                }
            };
        }
        return fSelectionListener;
    }

    protected ModifyListener getTextModifyListener() {
        if (fTextModifyListener == null) {
            fTextModifyListener = new ModifyListener() {

                public void modifyText(ModifyEvent e) {
                    textChanged((Text) e.widget);
                }
            };
        }
        return fTextModifyListener;
    }

    protected void controlChanged(Widget widget) {

        ControlData data = (ControlData) widget.getData();
        String newValue = null;
        if (widget instanceof Button) {
            if ((((Button) widget).getStyle() & SWT.RADIO) != 0) {
                RadioComposite parent = (RadioComposite) ((Button) widget).getParent();
                data = (ControlData) parent.getData();
                newValue = data.getValue(parent.getBtnIndex((Button) widget));
            } else {
                newValue = data.getValue(((Button) widget).getSelection());
            }

        } else if (widget instanceof Combo) {
            newValue = data.getValue(((Combo) widget).getSelectionIndex());
        } else {
            return;
        }
        String oldValue = setValue(data.getKey(), newValue);
        validateSettings(data.getKey(), oldValue, newValue);
    }

    protected void textChanged(Text textControl) {
        Key key = (Key) textControl.getData();
        String number = textControl.getText();
        String oldValue = setValue(key, number);
        validateSettings(key, oldValue, number);
    }

    protected boolean checkValue(Key key, String value) {
        return value.equals(getValue(key));
    }

    protected String getValue(Key key) {
        if (fDisabledProjectSettings != null) {
            return (String) fDisabledProjectSettings.get(key);
        }
        return key.getStoredValue(fPref);
    }

    protected boolean getBooleanValue(Key key) {
        return Boolean.valueOf(getValue(key)).booleanValue();
    }

    protected String setValue(Key key, String value) {
        if (fDisabledProjectSettings != null) {
            return (String) fDisabledProjectSettings.put(key, value);
        }
        String oldValue = getValue(key);
        key.setStoredValue(fPref, value);
        return oldValue;
    }

    protected String setValue(Key key, boolean value) {
        return setValue(key, String.valueOf(value));
    }

    /**
     * Returns the value as actually stored in the preference store.
     * 
     * @param key
     * @return the value as actually stored in the preference store.
     */
    protected String getStoredValue(Key key) {
        return key.getStoredValue(fPref);
    }

    public void useProjectSpecificSettings(boolean enable) {
        boolean hasProjectSpecificOption = fDisabledProjectSettings == null;
        if (enable != hasProjectSpecificOption && fProject != null) {
            if (enable) {
                for (int i = 0; i < fAllKeys.length; i++) {
                    Key curr = fAllKeys[i];
                    String val = (String) fDisabledProjectSettings.get(curr);
                    curr.setStoredValue(fPref, val);
                }
                fDisabledProjectSettings = null;
                updateControls();
                validateSettings(null, null, null);
            } else {
                fDisabledProjectSettings = new IdentityHashMap();
                for (int i = 0; i < fAllKeys.length; i++) {
                    Key curr = fAllKeys[i];
                    String oldSetting = curr.getStoredValue(fPref);
                    fDisabledProjectSettings.put(curr, oldSetting);
                }
            }
        }
    }

    public boolean hasProjectSpecificOptions(IProject project) {
        if (project != null) {
            Key[] allKeys = fAllKeys;
            IPreferences prefs = PreferenceFactory.getInstance().getPreferences(this.fPlugin, project);
            for (int i = 0; i < allKeys.length; i++) {
                if (prefs.isDefault(allKeys[i].getName())) {
                    return true;
                }
            }
        }
        return false;
    }

    public boolean areSettingsEnabled() {
        return fDisabledProjectSettings == null || fProject == null;
    }

    public boolean performOk() {
        return performApply();
    }

    public boolean performApply() {
        return processChanges(); // apply directly
    }

    protected boolean processChanges() {
        if (fDisabledProjectSettings != null) {
            for (int i = 0; i < fAllKeys.length; i++) {
                if (fProject != null) {
                    if (ignoreKeys != null && Arrays.asList(ignoreKeys).contains(fAllKeys[i])) {
                        continue;
                    }
                }
                fAllKeys[i].setToDefault(fPref);
            }
        } else {

            for (int i = 0; i < fAllKeys.length; i++) {
                if (fProject != null) {
                    if (ignoreKeys != null && Arrays.asList(ignoreKeys).contains(fAllKeys[i])) {
                        continue;
                    }
                }
                fAllKeys[i].apply(fPref);
            }
        }
        try {
            fPref.save();
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    public void performDefaults() {
        for (int i = 0; i < fAllKeys.length; i++) {
            Key curr = fAllKeys[i];
            String defValue = fAllKeys[i].getDefaultValue(fPref);
            setValue(curr, defValue);
        }

        settingsUpdated();
        updateControls();
        validateSettings(null, null, null);
    }

    public void dispose() {
    }

    protected void updateControls() {
        // update the UI
        for (int i = fCheckBoxes.size() - 1; i >= 0; i--) {
            updateCheckBox((Button) fCheckBoxes.get(i));
        }
        for (int i = fComboBoxes.size() - 1; i >= 0; i--) {
            updateCombo((Combo) fComboBoxes.get(i));
        }
        for (int i = fTextBoxes.size() - 1; i >= 0; i--) {
            updateText((Text) fTextBoxes.get(i));
        }

        for (int i = fRadioButtons.size() - 1; i >= 0; i--) {
            updateRadioComposite((RadioComposite) fRadioButtons.get(i));
        }
    }

    protected void updateCombo(Combo curr) {
        ControlData data = (ControlData) curr.getData();

        String currValue = getValue(data.getKey());
        curr.select(data.getSelection(currValue));

        if (fProject != null) {
            if (ignoreKeys != null && Arrays.asList(ignoreKeys).contains(data.getKey())) {
                ControlEnableState.disable(curr);
                Control label = (Control) fLabels.get(curr);
                if (label != null)
                    ControlEnableState.disable(label);
            }
        }
    }

    protected void updateCheckBox(Button curr) {
        ControlData data = (ControlData) curr.getData();

        String currValue = getValue(data.getKey());
        curr.setSelection(data.getSelection(currValue) == 0);

        if (fProject != null) {
            if (ignoreKeys != null && Arrays.asList(ignoreKeys).contains(data.getKey())) {
                ControlEnableState.disable(curr);
            }
        }
    }

    protected void updateRadioComposite(RadioComposite curr) {
        ControlData data = (ControlData) curr.getData();

        String currValue = getValue(data.getKey());
        curr.setSelection(data.getSelection(currValue));

        if (fProject != null) {
            if (ignoreKeys != null && Arrays.asList(ignoreKeys).contains(data.getKey())) {
                ControlEnableState.disable(curr);
            }
        }
    }

    protected void updateText(Text curr) {
        Key key = (Key) curr.getData();

        String currValue = getValue(key);
        if (currValue != null) {
            curr.setText(currValue);
        }

        if (fProject != null) {
            if (ignoreKeys != null && Arrays.asList(ignoreKeys).contains(key)) {
                ControlEnableState.disable(curr);
                Control label = (Control) fLabels.get(curr);
                if (label != null)
                    ControlEnableState.disable(label);
            }
        }
    }

    protected Button getCheckBox(Key key) {
        for (int i = fCheckBoxes.size() - 1; i >= 0; i--) {
            Button curr = (Button) fCheckBoxes.get(i);
            ControlData data = (ControlData) curr.getData();
            if (key.equals(data.getKey())) {
                return curr;
            }
        }
        return null;
    }

    protected Combo getComboBox(Key key) {
        for (int i = fComboBoxes.size() - 1; i >= 0; i--) {
            Combo curr = (Combo) fComboBoxes.get(i);
            ControlData data = (ControlData) curr.getData();
            if (key.equals(data.getKey())) {
                return curr;
            }
        }
        return null;
    }

    protected Text getTextControl(Key key) {
        for (int i = fTextBoxes.size() - 1; i >= 0; i--) {
            Text curr = (Text) fTextBoxes.get(i);
            ControlData data = (ControlData) curr.getData();
            if (key.equals(data.getKey())) {
                return curr;
            }
        }
        return null;
    }

    protected Control findControl(Key key) {
        Combo comboBox = getComboBox(key);
        if (comboBox != null) {
            return comboBox;
        }
        Button checkBox = getCheckBox(key);
        if (checkBox != null) {
            return checkBox;
        }
        Text text = getTextControl(key);
        if (text != null) {
            return text;
        }
        return null;
    }

    protected void setComboEnabled(Key key, boolean enabled) {
        Combo combo = getComboBox(key);
        Label label = (Label) fLabels.get(combo);
        combo.setEnabled(enabled);
        label.setEnabled(enabled);
    }

    protected Text addLabelledTextField(Composite parent, String label, Key key, int textlimit, int indent,
            boolean dummy) {
        PixelConverter pixelConverter = new PixelConverter(parent);

        Label labelControl = new Label(parent, SWT.WRAP);
        labelControl.setText(label);
        labelControl.setLayoutData(new GridData());

        Text textBox = new Text(parent, SWT.BORDER | SWT.SINGLE);
        textBox.setData(key);
        textBox.setLayoutData(new GridData());

        fLabels.put(textBox, labelControl);

        String currValue = getValue(key);
        if (currValue != null) {
            textBox.setText(currValue);
        }
        textBox.addModifyListener(getTextModifyListener());

        GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
        if (textlimit != 0) {
            textBox.setTextLimit(textlimit);
            data.widthHint = pixelConverter.convertWidthInCharsToPixels(textlimit + 1);
        }
        data.horizontalIndent = indent;
        data.horizontalSpan = 2;
        textBox.setLayoutData(data);

        fTextBoxes.add(textBox);
        return textBox;
    }

    /*
     * (non-javadoc) Update fields and validate. @param changedKey Key that
     * changed, or null, if all changed.
     */
    protected void validateSettings(Key changedKey, String oldValue, String newValue) {

    }
}