de.uniluebeck.itm.spyglass.gui.configuration.PluginPreferencePage.java Source code

Java tutorial

Introduction

Here is the source code for de.uniluebeck.itm.spyglass.gui.configuration.PluginPreferencePage.java

Source

/*
 * --------------------------------------------------------------------------------
 * This file is part of the WSN visualization framework SpyGlass. Copyright (C)
 * 2004-2007 by the SwarmNet (www.swarmnet.de) project SpyGlass is free
 * software; you can redistribute it and/or modify it under the terms of the BSD
 * License. Refer to spyglass-licence.txt file in the root of the SpyGlass
 * source tree for further details.
 * --------------------------------------------------------------------------------
 */
package de.uniluebeck.itm.spyglass.gui.configuration;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import org.apache.log4j.Logger;
import org.eclipse.core.databinding.AggregateValidationStatus;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;

import de.uniluebeck.itm.spyglass.core.Spyglass;
import de.uniluebeck.itm.spyglass.plugin.Plugin;
import de.uniluebeck.itm.spyglass.util.SpyglassLoggerFactory;
import de.uniluebeck.itm.spyglass.xmlconfig.PluginXMLConfig;

// --------------------------------------------------------------------------------

/**
 * @author Daniel Bimschas, Dariush Forouher, Sebastian Ebers
 * 
 * @param <T>
 */
public abstract class PluginPreferencePage<PluginClass extends Plugin, ConfigClass extends PluginXMLConfig>
        extends AbstractDatabindingPreferencePage {

    private static Logger log = SpyglassLoggerFactory.getLogger(PluginPreferencePage.class);

    // --------------------------------------------------------------------------------
    /**
     * Enumeration. Decides, if the surrounding PluginPreferencesWidget represents an Type or an
     * Instance.
     */
    protected enum PrefType {
        INSTANCE, TYPE
    }

    protected enum BasicOptions {
        /**
         * Show all Fields in the the optionsGroup Basic
         */
        ALL,

        /**
         * Show all Fields except "isVisible" in the the optionsGroup Basic
         */
        ALL_BUT_VISIBLE,

        /**
         * Show all Fields except "isVisible" and fields for handling semantic types in the the
         * optionsGroup Basic
         */
        ALL_BUT_VISIBLE_AND_SEMANTIC_TYPES,

        /**
         * Show all Fields except fields for handling semantic types in the the optionsGroup Basic
         */
        ALL_BUT_SEMANTIC_TYPES

    }

    BasicOptions basicOptions = BasicOptions.ALL;

    private final SelectionListener buttonSelectionListener = new SelectionAdapter() {
        @SuppressWarnings("synthetic-access")
        @Override
        public void widgetSelected(final SelectionEvent e) {
            if (e.getSource() == buttons.deleteButton) {
                performDelete();
            } else if (e.getSource() == buttons.restoreButton) {
                performRestore();
            } else if (e.getSource() == buttons.applyButton) {
                PluginPreferencePage.super.performApply();
            } else if (e.getSource() == buttons.restoreDefaultsButton) {
                performRestoreDefaults();
            } else if (e.getSource() == buttons.saveAsDefaultButton) {
                PluginPreferencePage.this.performApply();
            } else if (e.getSource() == buttons.createInstanceButton) {
                performCreateInstance();
            }
        }
    };

    /**
     * Reference to the plugin instance. may be null if PrefType==TYPE.
     */
    protected final PluginClass plugin;

    /**
     * Temporal config. it contains the current settings on the preference page, before the the user
     * pressed "Apply".
     * 
     * This field is final, since databinding listens to events from this object specifically.
     */
    protected final ConfigClass config;

    /**
     * Default configuration of the {@link PluginPreferencePage#plugin}
     */
    private final ConfigClass defaultConfig;

    /**
     * is this page representing an plugin type or instance?
     */
    protected PrefType type;

    /**
     * reference to spyglass
     */
    protected final Spyglass spyglass;

    final PropertyChangeListener propertyChangeListener;

    private class Buttons {

        private Button restoreButton;
        private Button restoreDefaultsButton;
        private Button saveAsDefaultButton;
        private Button deleteButton;
        private Button createInstanceButton;
        private Button applyButton;

    }

    private Buttons buttons = new Buttons();

    private BasicGroupComposite basicGroup;

    // --------------------------------------------------------------------------------
    /**
     * Create a preference page for editing the defaultsconfiguration of an plugin type.
     * 
     * @param cs
     */
    @SuppressWarnings("unchecked")
    public PluginPreferencePage(final PluginPreferenceDialog dialog, final Spyglass spyglass,
            final BasicOptions basicOptions) {
        super();
        noDefaultAndApplyButton();
        this.type = PrefType.TYPE;
        // this.dialog = dialog;
        this.spyglass = spyglass;
        this.basicOptions = basicOptions;
        this.plugin = null;

        // propertyChangeListener not needed since it's used for
        // instances updating their labels in preference tree
        this.propertyChangeListener = null;

        // This is fine
        defaultConfig = (ConfigClass) spyglass.getConfigStore().getSpyglassConfig()
                .getDefaultConfig(this.getPluginClass());
        if (defaultConfig == null) {
            // this page represents an abstract plugin type. so no config here
            config = null;
        } else {
            config = (ConfigClass) defaultConfig.clone();
        }

    }

    // --------------------------------------------------------------------------------
    /**
     * Create a preference page for editing the configuration of an plugin instance.
     * 
     * @param cs
     * @param plugin
     */
    @SuppressWarnings("unchecked")
    public PluginPreferencePage(final PluginPreferenceDialog dialog, final Spyglass spyglass,
            final PluginClass plugin, final BasicOptions basicOptions) {
        super();
        noDefaultAndApplyButton();
        this.type = PrefType.INSTANCE;
        // this.dialog = dialog;
        this.spyglass = spyglass;
        this.plugin = plugin;
        this.basicOptions = basicOptions;

        this.config = (ConfigClass) plugin.getXMLConfig();
        defaultConfig = null;

        this.propertyChangeListener = new PropertyChangeListener() {
            @Override
            public void propertyChange(final PropertyChangeEvent evt) {
                dialog.onPluginInstancePropertyChange();
            }
        };

        this.config.addPropertyChangeListener("active", propertyChangeListener);
        this.config.addPropertyChangeListener("visible", propertyChangeListener);
        this.config.addPropertyChangeListener("name", propertyChangeListener);

    }

    @Override
    protected void contributeButtons(final Composite parent) {

        if (this.config == null) {
            // this means that the plugin type is abstract
            return;
        }

        if (type == PrefType.INSTANCE) {

            buttons.deleteButton = createButton(parent, "Delete Instance", buttonSelectionListener);
            buttons.restoreButton = createButton(parent, "Restore Values", buttonSelectionListener);
            buttons.applyButton = createButton(parent, "Apply", buttonSelectionListener);

            if (!this.isValid()) {
                buttons.applyButton.setEnabled(false);
            }

        } else {

            buttons.restoreDefaultsButton = createButton(parent, "Restore Defaults", buttonSelectionListener);
            buttons.saveAsDefaultButton = createButton(parent, "Save as Default", buttonSelectionListener);
            buttons.createInstanceButton = createButton(parent, "Create Instance", buttonSelectionListener);

            if (!this.isValid()) {
                buttons.saveAsDefaultButton.setEnabled(false);
            }
            if (!this.isValid()) {
                buttons.createInstanceButton.setEnabled(false);
            }
        }

    }

    @Override
    protected Composite createContents(final Composite parent) {
        return createContentsInternal(parent);
    }

    @Override
    public void createControl(final Composite parent) {
        super.createControl(parent);
        resetDirtyFlag();
    }

    @Override
    protected void resetDirtyFlag() {
        super.resetDirtyFlag();

        if (this.config == null) {
            // this means that the plugin type is abstract
            return;
        }

        if (this.isInstancePage()) {
            buttons.applyButton.setEnabled(false);
            buttons.restoreButton.setEnabled(false);
        } else {
            buttons.restoreDefaultsButton.setEnabled(false);
            buttons.saveAsDefaultButton.setEnabled(false);
        }
    }

    @Override
    protected Composite createContentsInternal(final Composite parent) {

        final Composite composite = super.createContentsInternal(parent);

        if (this.config == null) {
            // this means that the plugin type is abstract
            return composite;
        }

        basicGroup = new BasicGroupComposite(composite, SWT.NONE);
        basicGroup.disableUnwantedElements(basicOptions);
        basicGroup.setDatabinding(dbc, config, this.plugin, this.spyglass.getPluginManager(),
                this.isInstancePage());

        return composite;
    }

    /**
     * Adds the handler to the ValidationStatus provider of the DataBindingCotext. Whenever the
     * validation status changes, the handler will update the errorString displayed to the user and
     * set a flag variable.
     * 
     * the handler will also grey out the apply-Button, if there are errors present.
     * 
     */
    @Override
    protected AggregateValidationStatus addErrorBinding() {
        final AggregateValidationStatus aggregateStatus = super.addErrorBinding();

        aggregateStatus.addValueChangeListener(new IValueChangeListener() {

            public void handleValueChange(final ValueChangeEvent event) {

                final Status valStatus = (Status) event.diff.getNewValue();

                if (valStatus.getSeverity() != IStatus.ERROR) {

                    if (buttons.applyButton != null) {
                        buttons.applyButton.setEnabled(true);
                    }
                    if (buttons.createInstanceButton != null) {
                        buttons.createInstanceButton.setEnabled(true);
                    }
                    if (buttons.saveAsDefaultButton != null) {
                        buttons.saveAsDefaultButton.setEnabled(true);
                    }
                } else {

                    if (buttons.applyButton != null) {
                        buttons.applyButton.setEnabled(false);
                    }
                    if (buttons.createInstanceButton != null) {
                        buttons.createInstanceButton.setEnabled(false);
                    }
                    if (buttons.saveAsDefaultButton != null) {
                        buttons.saveAsDefaultButton.setEnabled(false);
                    }
                }

            }
        });

        return aggregateStatus;

    }

    private Button createButton(final Composite parent, final String label,
            final SelectionListener selectionListener) {
        ((GridLayout) parent.getLayout()).numColumns++;
        final Button button = new Button(parent, SWT.PUSH);
        button.setText(label);
        button.setFont(JFaceResources.getDialogFont());
        button.addSelectionListener(selectionListener);
        setButtonLayoutData(button);
        return button;
    }

    private final void performCreateInstance() {
        log.info("Pressed button create");

        final boolean isDirty = hasUnsavedChanges();

        // First save data.
        super.performApply();
        if (!this.isValid()) {
            MessageDialog.openError(this.getShell(), "Can not store changes",
                    "Could not store your changes. There are still errors remaining in the form.");
        } else {

            try {
                spyglass.getPluginManager().createNewPlugin(getPluginClass(), config.clone());
            } catch (final Exception e) {
                log.error("Could not create the requested plugin.", e);
            }
        }

        if (isDirty && (defaultConfig != null)) {
            markFormDirty();
        }
    }

    // --------------------------------------------------------------------------------
    @Override
    public void performApply() {
        super.performApply();
        if (defaultConfig != null) {
            defaultConfig.overwriteWith(config);
        }
    }

    // --------------------------------------------------------------------------------
    @Override
    protected void loadFromModel() {
        if (defaultConfig != null) {
            config.overwriteWith(defaultConfig);
        }
        super.loadFromModel();
    }

    /**
     * Delete the plugin
     */
    private final void performDelete() {
        log.info("Pressed button Delete");

        final boolean ok = MessageDialog.openQuestion(getShell(), "Remove plugin instance",
                "Are you sure you want to remove the plugin instance?");
        if (ok) {
            final boolean ret = spyglass.getPluginManager().removePlugin(this.plugin);
            if (!ret) {
                MessageDialog.openError(this.getShell(), "Cannot delete plugin", "Could not delete the plugin.");
            }
        }
    }

    // --------------------------------------------------------------------------------
    /**
     * @return
     */
    protected final void performRestoreDefaults() {
        log.info("Pressed button restoreDefaults");
        // TODO: vorher fragen?
        final Class<? extends Plugin> pluginClass = this.getPluginClass();
        final PluginXMLConfig defaults = spyglass.getConfigStore().getSpyglassConfig()
                .getDefaultConfig(pluginClass);
        this.config.overwriteWith(defaults);

        this.loadFromModel();

    }

    // --------------------------------------------------------------------------------
    /**
     * 
     * @return
     */
    protected final void performRestore() {
        log.info("Pressed button restore");
        loadFromModel();
    }

    // --------------------------------------------------------------------------------
    /**
     * Checks if this is an instance page or a type page.
     * 
     * @return <code>true</code> if this is a preference page for a plugin instance,
     *         <code>false</code> if this is a preference page for an instantiable plugin type.
     */
    public final boolean isInstancePage() {
        return type == PrefType.INSTANCE;
    }

    // --------------------------------------------------------------------------------
    /**
     * Returns the <code>Plugin</code> instance associated with this page.
     * 
     * @return the associated <code>Plugin</code> instance or <code>null</code> if this is a type
     *         page (i.e. not an instance page, also see
     *         {@link PluginPreferencePage#isInstancePage()})
     */
    public final Plugin getPlugin() {
        return plugin;
    }

    // --------------------------------------------------------------------------------
    /**
     * Returns the class-Object of the plugin this preference page is associated with. Needed for
     * runtime-reflection.
     * 
     * @return the class-Object of the plugin this preference page is associated with
     */
    public abstract Class<? extends Plugin> getPluginClass();

    // --------------------------------------------------------------------------------
    /**
     * Returns the class-Object of the plugins' PluginXMLConfig this preference page is associated
     * with. Needed for runtime reflection.
     * 
     * @return the class-Object of the plugins' PluginXMLConfig this preference page is associated
     *         with
     */
    public final Class<? extends PluginXMLConfig> getConfigClass() {
        return this.config.getClass();
    }

    public void removePropertyChangeListeners() {
        if (config != null) {
            config.removePropertyChangeListener(PluginXMLConfig.PROPERTYNAME_ACTIVE, propertyChangeListener);
            config.removePropertyChangeListener(PluginXMLConfig.PROPERTYNAME_VISIBLE, propertyChangeListener);
            config.removePropertyChangeListener(PluginXMLConfig.PROPERTYNAME_NAME, propertyChangeListener);
        }
    }

    @Override
    public void dispose() {
        removePropertyChangeListeners();
    }

    /**
     * Calling this method marks the form dirty (and thus enables the "Apply" button)
     */
    @Override
    public void markFormDirty() {
        super.markFormDirty();

        if ((config == null) || !isValid()) {
            // this means that the plugin type is abstractor contains errors
            return;
        }

        if (isInstancePage()) {
            if (buttons.applyButton != null) {
                buttons.applyButton.setEnabled(true);
            }
            if (buttons.restoreButton != null) {
                buttons.restoreButton.setEnabled(true);
            }
        } else {
            if (buttons.restoreDefaultsButton != null) {
                buttons.restoreDefaultsButton.setEnabled(true);
            }
            if (buttons.saveAsDefaultButton != null) {
                buttons.saveAsDefaultButton.setEnabled(true);
            }
        }

    }

}