de.walware.rj.eclient.graphics.RGraphicsPreferencePage.java Source code

Java tutorial

Introduction

Here is the source code for de.walware.rj.eclient.graphics.RGraphicsPreferencePage.java

Source

/*=============================================================================#
 # Copyright (c) 2009-2015 Stephan Wahlbrink (WalWare.de) 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:
 #     Stephan Wahlbrink - initial API and implementation
 #=============================================================================*/

package de.walware.rj.eclient.graphics;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.databinding.AggregateValidationStatus;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
import org.eclipse.core.databinding.observable.value.WritableValue;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IScopeContext;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FontDialog;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.statushandlers.StatusManager;
import org.osgi.service.prefs.BackingStoreException;

import de.walware.ecommons.databinding.DecimalValidator;
import de.walware.ecommons.ui.components.StatusInfo;
import de.walware.ecommons.ui.util.DialogUtil;
import de.walware.ecommons.ui.util.LayoutUtil;

/**
 * Preference page with options to configure R graphic options:
 * <ul>
 *    <li>Default font families ('serif', 'sans', 'mono')</li>
 * </ul>
 * 
 * The page is not registered by this plug-in.
 */
public class RGraphicsPreferencePage extends PreferencePage implements IWorkbenchPreferencePage {

    public static double[] parseDPI(final String prefValue) {
        if (prefValue != null) {
            final String[] strings = prefValue.split(",");
            if (strings.length == 2) {
                try {
                    return new double[] { Double.parseDouble(strings[0]), Double.parseDouble(strings[1]), };
                } catch (final Exception e) {
                }
            }
        }
        return null;
    }

    private static class FontPref {

        final String prefKey;

        String defaultName;
        String currentName;

        Font currentFont;

        Label valueLabel;

        public FontPref(final String key) {
            prefKey = key;
        }

        @Override
        public int hashCode() {
            return prefKey.hashCode();
        }

        @Override
        public boolean equals(final Object obj) {
            return (this == obj || (obj instanceof FontPref && prefKey.equals(((FontPref) obj).prefKey)));
        }

    }

    private static class Encoding {

        final String label;
        final String prefValue;

        public Encoding(final String label, final String prefValue) {
            this.label = label;
            this.prefValue = prefValue;
        }

        @Override
        public int hashCode() {
            return prefValue.hashCode();
        }

        @Override
        public boolean equals(final Object obj) {
            return (this == obj || (obj instanceof Encoding && prefValue.equals(((Encoding) obj).prefValue)));
        }

        @Override
        public String toString() {
            return label.toString();
        };

    }

    private static final Encoding[] SYMBOL_ENCODINGS = new Encoding[] { new Encoding("Unicode", "Unicode"),
            new Encoding("Adobe Symbol", "AdobeSymbol"), };

    private static final Encoding SYMBOL_ENCODING_DEFAULT = SYMBOL_ENCODINGS[1];

    private DataBindingContext fDbc;

    private FontPref fSerifPref;
    private FontPref fSansPref;
    private FontPref fMonoPref;
    private FontPref fSymbolFontPref;
    private FontPref[] fFontPrefs;
    private Button fSymbolUseControl;
    private ComboViewer fSymbolEncodingControl;
    private Control[] fSymbolChildControls;

    private final int fSize = 10;

    private Button fCustomDpiControl;
    private Composite fCustomDpiComposite;
    private Text fCustomHDpiControl;
    private Text fCustomVDpiControl;

    private boolean fCustomEnabled;
    private WritableValue fCustomHDpiVisibleValue;
    private WritableValue fCustomVDpiVisibleValue;
    private double fCustomHDpiUserValue;
    private double fCustomVDpiUserValue;

    /**
     * Created via extension point
     */
    public RGraphicsPreferencePage() {
    }

    @Override
    public void init(final IWorkbench workbench) {
        fSerifPref = new FontPref(RGraphics.PREF_FONTS_SERIF_FONTNAME_KEY);
        fSansPref = new FontPref(RGraphics.PREF_FONTS_SANS_FONTNAME_KEY);
        fMonoPref = new FontPref(RGraphics.PREF_FONTS_MONO_FONTNAME_KEY);
        fSymbolFontPref = new FontPref(RGraphics.PREF_FONTS_SYMBOL_FONTNAME_KEY);
        fFontPrefs = new FontPref[] { fSerifPref, fSansPref, fMonoPref, fSymbolFontPref };

        final IEclipsePreferences node = new DefaultScope().getNode(RGraphics.PREF_FONTS_QUALIFIER);
        for (final FontPref pref : fFontPrefs) {
            pref.defaultName = (node != null) ? node.get(pref.prefKey, "") : "";
        }
    }

    @Override
    protected Control createContents(final Composite parent) {
        final Composite pageComposite = new Composite(parent, SWT.NONE);
        pageComposite.setLayout(LayoutUtil.applyCompositeDefaults(new GridLayout(), 1));

        final Group displayGroup = createDisplayGroup(pageComposite);
        displayGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));

        final Group fontGroup = createFontGroup(pageComposite);
        fontGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));

        initBindings();
        loadDisplayOptions();
        loadFontOptions();

        applyDialogFont(pageComposite);

        return pageComposite;
    }

    protected Group createDisplayGroup(final Composite parent) {
        final Group group = new Group(parent, SWT.NONE);
        group.setText("Display:");
        group.setLayout(LayoutUtil.applyGroupDefaults(new GridLayout(), 2));

        fCustomDpiControl = new Button(group, SWT.CHECK);
        fCustomDpiControl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
        fCustomDpiControl.setText("Use custom DPI (instead of system setting):");

        fCustomDpiComposite = new Composite(group, SWT.NONE);

        {
            final GridData gd = new GridData(SWT.FILL, SWT.FILL, true, false);
            gd.horizontalIndent = LayoutUtil.defaultIndent();
            fCustomDpiComposite.setLayoutData(gd);
            fCustomDpiComposite.setLayout(LayoutUtil.applyCompositeDefaults(new GridLayout(), 2));
        }

        {
            final Label label = new Label(fCustomDpiComposite, SWT.LEFT);
            label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
            label.setText("&Horizontal (x):");
        }
        {
            final Text text = new Text(fCustomDpiComposite, SWT.BORDER | SWT.RIGHT);
            final GridData gd = new GridData(SWT.LEFT, SWT.CENTER, false, false);
            gd.widthHint = LayoutUtil.hintWidth(text, 8);
            text.setLayoutData(gd);
            fCustomHDpiControl = text;
        }
        {
            final Label label = new Label(fCustomDpiComposite, SWT.LEFT);
            label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
            label.setText("&Vertical (y):");
        }
        {
            final Text text = new Text(fCustomDpiComposite, SWT.BORDER | SWT.RIGHT);
            final GridData gd = new GridData(SWT.LEFT, SWT.CENTER, false, false);
            gd.widthHint = LayoutUtil.hintWidth(text, 8);
            text.setLayoutData(gd);
            fCustomVDpiControl = text;
        }

        fCustomDpiControl.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                updateDisplayGroup();
            }
        });

        return group;
    }

    private void updateDisplayGroup() {
        fCustomEnabled = fCustomDpiControl.getSelection();
        DialogUtil.setEnabled(fCustomDpiComposite, null, fCustomEnabled);

        final double hvalue;
        final double vvalue;
        if (!fCustomEnabled || fCustomHDpiUserValue <= 0 || fCustomVDpiUserValue <= 0) {
            final Point dpi = Display.getDefault().getDPI();
            hvalue = dpi.x;
            vvalue = dpi.y;
            if (fCustomEnabled) {
                fCustomHDpiUserValue = hvalue;
                fCustomVDpiUserValue = vvalue;
            }
        } else {
            hvalue = fCustomHDpiUserValue;
            vvalue = fCustomVDpiUserValue;
        }
        fCustomHDpiVisibleValue.setValue(hvalue);
        fCustomVDpiVisibleValue.setValue(vvalue);
    }

    protected Group createFontGroup(final Composite parent) {
        final Group group = new Group(parent, SWT.NONE);
        group.setText("Fonts:");
        group.setLayout(LayoutUtil.applyGroupDefaults(new GridLayout(), 3));

        addFont(group, fSerifPref, "Default &Serif Font ('serif'):");
        addFont(group, fSansPref, "Default S&ansserif Font ('sans'):");
        addFont(group, fMonoPref, "Default &Monospace Font ('mono'):");

        LayoutUtil.addSmallFiller(group, false);

        fSymbolUseControl = new Button(group, SWT.CHECK);
        fSymbolUseControl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 3, 1));
        fSymbolUseControl.setText("Use special S&ymbol Font");
        final List<Control> symbolControls = new ArrayList<Control>();
        addFont(group, fSymbolFontPref, "Symbol &Font:", LayoutUtil.defaultIndent(), symbolControls);
        {
            final Label label = new Label(group, SWT.NONE);
            final GridData gd = new GridData(SWT.FILL, SWT.CENTER, false, false);
            gd.horizontalIndent = LayoutUtil.defaultIndent();
            label.setLayoutData(gd);
            label.setText("Encoding:");
            symbolControls.add(label);
        }
        {
            fSymbolEncodingControl = new ComboViewer(group, SWT.DROP_DOWN | SWT.READ_ONLY);
            final GridData gd = new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1);
            gd.widthHint = LayoutUtil.hintWidth(fSymbolEncodingControl.getCombo(), 15);
            fSymbolEncodingControl.getControl().setLayoutData(gd);
            fSymbolEncodingControl.setContentProvider(new ArrayContentProvider());
            fSymbolEncodingControl.setInput(SYMBOL_ENCODINGS);
            symbolControls.add(fSymbolEncodingControl.getControl());
        }
        fSymbolChildControls = symbolControls.toArray(new Control[symbolControls.size()]);
        fSymbolUseControl.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                updateSymbolControls();
            }
        });

        return group;
    }

    private void addFont(final Group group, final FontPref pref, final String text) {
        addFont(group, pref, text, 0, null);
    }

    private void addFont(final Group group, final FontPref pref, final String text, final int indent,
            final List<Control> controls) {
        final Label label = new Label(group, SWT.NONE);
        final GridData gd = new GridData(SWT.FILL, SWT.CENTER, false, false);
        gd.horizontalIndent = indent;
        label.setLayoutData(gd);
        label.setText(text);

        pref.valueLabel = new Label(group, SWT.BORDER);
        pref.valueLabel.setBackground(label.getDisplay().getSystemColor(SWT.COLOR_WHITE));
        pref.valueLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));

        final Button button = new Button(group, SWT.PUSH);
        button.setText("Edit...");
        button.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
        button.addSelectionListener(new SelectionListener() {
            @Override
            public void widgetSelected(final SelectionEvent e) {
                final FontDialog dialog = new FontDialog(button.getShell(), SWT.NONE);
                dialog.setFontList((pref.currentFont != null) ? pref.currentFont.getFontData() : null);
                final FontData result = dialog.open();
                if (result != null) {
                    set(pref, result.getName());
                }
            }

            @Override
            public void widgetDefaultSelected(final SelectionEvent e) {
            }
        });

        if (controls != null) {
            controls.add(label);
            controls.add(pref.valueLabel);
            controls.add(button);
        }
    }

    protected void initBindings() {
        final Realm realm = Realm.getDefault();
        fDbc = new DataBindingContext(realm);
        addBindings(fDbc, realm);

        final AggregateValidationStatus validationStatus = new AggregateValidationStatus(fDbc,
                AggregateValidationStatus.MAX_SEVERITY);
        validationStatus.addValueChangeListener(new IValueChangeListener() {
            @Override
            public void handleValueChange(final ValueChangeEvent event) {
                final IStatus currentStatus = (IStatus) event.diff.getNewValue();
                updateStatus(currentStatus);
            }
        });
    }

    protected void addBindings(final DataBindingContext dbc, final Realm realm) {
        fCustomHDpiVisibleValue = new WritableValue(realm, 96.0, Double.class);
        fCustomVDpiVisibleValue = new WritableValue(realm, 96.0, Double.class);

        dbc.bindValue(SWTObservables.observeText(fCustomHDpiControl, SWT.Modify), fCustomHDpiVisibleValue,
                new UpdateValueStrategy().setAfterGetValidator(new DecimalValidator(10.0, 10000.0,
                        "The value for Horizontal (x) DPI is invalid (10-10000).")),
                null);
        dbc.bindValue(SWTObservables.observeText(fCustomVDpiControl, SWT.Modify), fCustomVDpiVisibleValue,
                new UpdateValueStrategy().setAfterGetValidator(new DecimalValidator(10.0, 10000.0,
                        "The value for Vertical (x) DPI is invalid (10-10000).")),
                null);

        fCustomHDpiVisibleValue.addValueChangeListener(new IValueChangeListener() {
            @Override
            public void handleValueChange(final ValueChangeEvent event) {
                if (fCustomEnabled) {
                    fCustomHDpiUserValue = (Double) fCustomHDpiVisibleValue.getValue();
                }
            }
        });
        fCustomVDpiVisibleValue.addValueChangeListener(new IValueChangeListener() {
            @Override
            public void handleValueChange(final ValueChangeEvent event) {
                if (fCustomEnabled) {
                    fCustomVDpiUserValue = (Double) fCustomVDpiVisibleValue.getValue();
                }
            }
        });
    }

    protected void updateStatus(final IStatus status) {
        setValid(!status.matches(IStatus.ERROR));
        StatusInfo.applyToStatusLine(this, status);
    }

    protected void setCustomDpi(final String prefValue) {
        final double[] dpi = parseDPI(prefValue);
        if (dpi != null) {
            fCustomHDpiUserValue = dpi[0];
            fCustomVDpiUserValue = dpi[1];
            fCustomDpiControl.setSelection(true);
        } else {
            fCustomDpiControl.setSelection(false);
        }
        updateDisplayGroup();
    }

    protected void updateSymbolControls() {
        DialogUtil.setEnabled(fSymbolChildControls, null, fSymbolUseControl.getSelection());
    }

    protected void set(final FontPref pref, final String value) {
        if (value.equals(pref.currentName)) {
            return;
        }
        pref.valueLabel.setText("");
        Font font;
        try {
            font = new Font(pref.valueLabel.getDisplay(), value, fSize, SWT.NONE);
            if (pref != fSymbolFontPref) {
                pref.valueLabel.setFont(font);
            }
            pref.valueLabel.setText(value);
        } catch (final SWTError e) {
            font = JFaceResources.getDialogFont();
            pref.valueLabel.setFont(font);
            pref.valueLabel.setText(value + " (not available)");
        }
        if (pref.currentFont != null && !pref.currentFont.isDisposed()) {
            pref.currentFont.dispose();
        }
        pref.currentName = value;
        pref.currentFont = font;
    }

    @Override
    protected void performDefaults() {
        setCustomDpi(null);
        fSymbolUseControl.setSelection(true);
        fSymbolEncodingControl.setSelection(new StructuredSelection(SYMBOL_ENCODING_DEFAULT));
        for (final FontPref pref : fFontPrefs) {
            set(pref, pref.defaultName);
        }
        updateSymbolControls();
        super.performDefaults();
    }

    @Override
    protected void performApply() {
        saveDisplayOptions(true);
        saveFontOptions(true);
    }

    @Override
    public boolean performOk() {
        saveDisplayOptions(false);
        saveFontOptions(false);
        return true;
    }

    protected IScopeContext getScope() {
        return new InstanceScope();
    }

    protected void loadDisplayOptions() {
        final IEclipsePreferences node = getScope().getNode(RGraphics.PREF_DISPLAY_QUALIFIER);
        setCustomDpi(node.get(RGraphics.PREF_DISPLAY_CUSTOM_DPI_KEY, null));
    }

    protected void saveDisplayOptions(final boolean flush) {
        final IEclipsePreferences node = getScope().getNode(RGraphics.PREF_DISPLAY_QUALIFIER);
        if (fCustomEnabled) {
            node.put(RGraphics.PREF_DISPLAY_CUSTOM_DPI_KEY,
                    Double.toString(fCustomHDpiUserValue) + "," + Double.toString(fCustomVDpiUserValue));
        } else {
            node.remove(RGraphics.PREF_DISPLAY_CUSTOM_DPI_KEY);
        }
        if (flush) {
            try {
                node.flush();
            } catch (final BackingStoreException e) {
                StatusManager.getManager().handle(new Status(IStatus.ERROR, RGraphics.PLUGIN_ID, -1,
                        "An error occurred when storing R graphics display preferences.", e));
            }
        }
    }

    protected void loadFontOptions() {
        if (fFontPrefs != null) {
            final IEclipsePreferences node = getScope().getNode(RGraphics.PREF_FONTS_QUALIFIER);
            fSymbolUseControl.setSelection(node.getBoolean(RGraphics.PREF_FONTS_SYMBOL_USE_KEY, true));
            final String s = node.get(RGraphics.PREF_FONTS_SYMBOL_ENCODING_KEY, (String) null);
            fSymbolEncodingControl.setSelection(
                    new StructuredSelection((s != null) ? new Encoding(null, s) : SYMBOL_ENCODING_DEFAULT));
            for (final FontPref pref : fFontPrefs) {
                final String value = node.get(pref.prefKey, ""); //$NON-NLS-1$
                set(pref, (value.length() > 0) ? value : pref.defaultName);
            }
            updateSymbolControls();
        }
    }

    protected void saveFontOptions(final boolean flush) {
        if (fFontPrefs != null) {
            final IEclipsePreferences node = getScope().getNode(RGraphics.PREF_FONTS_QUALIFIER);
            node.putBoolean(RGraphics.PREF_FONTS_SYMBOL_USE_KEY, fSymbolUseControl.getSelection());
            final IStructuredSelection selection = (IStructuredSelection) fSymbolEncodingControl.getSelection();
            if (selection.getFirstElement() instanceof Encoding
                    && !SYMBOL_ENCODING_DEFAULT.equals(selection.getFirstElement())) {
                node.put(RGraphics.PREF_FONTS_SYMBOL_ENCODING_KEY,
                        ((Encoding) selection.getFirstElement()).prefValue);
            } else {
                node.remove(RGraphics.PREF_FONTS_SYMBOL_ENCODING_KEY);
            }
            for (final FontPref pref : fFontPrefs) {
                if (pref.currentName == null || pref.currentName.equals(pref.defaultName)) {
                    node.remove(pref.prefKey);
                } else {
                    node.put(pref.prefKey, pref.currentName);
                }
            }
            updateSymbolControls();
            if (flush) {
                try {
                    node.flush();
                } catch (final BackingStoreException e) {
                    StatusManager.getManager().handle(new Status(IStatus.ERROR, RGraphics.PLUGIN_ID, -1,
                            "An error occurred when storing R graphics font preferences.", e));
                }
            }
        }
    }

    @Override
    public void dispose() {
        if (fDbc != null) {
            fDbc.dispose();
            fDbc = null;
        }
        if (fFontPrefs != null) {
            for (final FontPref pref : fFontPrefs) {
                if (pref.currentFont != null && !pref.currentFont.isDisposed()) {
                    pref.currentFont.dispose();
                    pref.currentFont = null;
                }
            }
        }
        super.dispose();
    }

}