/*=============================================================================# # Copyright (c) 2007-2016 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.statet.r.internal.ui; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import com.ibm.icu.text.Collator; import org.eclipse.core.databinding.Binding; import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.observable.Diffs; import org.eclipse.core.databinding.observable.Realm; import org.eclipse.core.databinding.observable.list.IObservableList; import org.eclipse.core.databinding.observable.value.AbstractObservableValue; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.core.databinding.validation.IValidator; import org.eclipse.core.databinding.validation.ValidationStatus; import org.eclipse.core.runtime.IStatus; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Text; import de.walware.jcommons.collections.ImCollections; import de.walware.jcommons.collections.ImIdentitySet; import de.walware.ecommons.FastList; import de.walware.ecommons.preferences.core.IPreferenceAccess; import de.walware.ecommons.preferences.core.IPreferenceSetService.IChangeEvent; import de.walware.ecommons.preferences.core.util.PreferenceUtils; import de.walware.ecommons.preferences.ui.PreferenceSetUIListener; import de.walware.ecommons.ui.util.LayoutUtil; import de.walware.statet.r.core.RCore; import de.walware.statet.r.core.renv.IREnv; import de.walware.statet.r.core.renv.IREnvConfiguration; import de.walware.statet.r.core.renv.IREnvManager; import de.walware.statet.r.core.renv.REnvUtil; import de.walware.statet.r.internal.debug.ui.preferences.REnvPreferencePage; /** * Composite to choose a configured R Environment. */ public class REnvSelectionComposite extends Composite { private static final ImIdentitySet<String> PREF_QUALIFIERS= ImCollections.newIdentitySet( IREnvManager.PREF_QUALIFIER ); private static final Comparator<IREnv> RENV_COMPARATOR = new Comparator<IREnv>() { @Override public int compare(final IREnv o1, final IREnv o2) { return Collator.getInstance().compare(o1.getName(), o2.getName()); } }; public static interface ChangeListener { public void settingChanged(REnvSelectionComposite source, String oldValue, String newValue, IREnv newREnv); } private class CompositeObservable extends AbstractObservableValue implements ChangeListener { public CompositeObservable(final Realm realm) { super(realm); REnvSelectionComposite.this.addChangeListener(CompositeObservable.this); } @Override public void settingChanged(final REnvSelectionComposite source, final String oldValue, final String newValue, final IREnv newREnv) { fireValueChange(Diffs.createValueDiff(oldValue, newValue)); } @Override protected void doSetValue(final Object value) { setEncodedSetting((String) value); } @Override protected Object doGetValue() { return getEncodedSetting(); } @Override public Object getValueType() { return String.class; } public REnvSelectionComposite getComposite() { return REnvSelectionComposite.this; } } private class ChooseREnvValidator implements IValidator { @Override public IStatus validate(final Object dummy) { if (fInvalidPreference) { return ValidationStatus.error(RUIMessages.ChooseREnv_error_InvalidPreferences_message); } if (fCurrentREnv == null) { if (fEnableNone) { return ValidationStatus.ok(); } return ValidationStatus.error(RUIMessages.ChooseREnv_error_IncompleteSelection_message); } final IREnv rEnv = fCurrentREnv.resolve(); if (rEnv == null || (fValidREnvs != null && !fValidREnvs.contains(rEnv)) || rEnv.getConfig() == null) { return ValidationStatus.error(RUIMessages.ChooseREnv_error_InvalidSelection_message); } return ValidationStatus.ok(); } } private final IPreferenceAccess prefAccess; private final boolean fEnableNone; private boolean fInvalidPreference; private List<IREnv> fValidREnvs; private IREnv fCurrentREnv; private String fCurrentEncoded; private IREnv fCurrentSpecified; private final FastList<ChangeListener> fListeners= new FastList<>(ChangeListener.class); private DataBindingContext fBindindContexts; private Binding fBindings; private Button fNoneButton; private Button fWorkbenchDefaultButton; private Text fWorkbenchLabel; private Button fSpecificButton; private Combo fSpecificCombo; private Button fConfigurationButton; public REnvSelectionComposite(final Composite parent) { this(parent, false); } public REnvSelectionComposite(final Composite parent, final boolean enableNone) { super(parent, SWT.NONE); fEnableNone = enableNone; fInvalidPreference = true; createControls(); fWorkbenchDefaultButton.setSelection(true); this.prefAccess= PreferenceUtils.getInstancePrefs(); initPreferences(); updateState(true, false); } private void initPreferences() { new PreferenceSetUIListener(this.prefAccess, this.fSpecificCombo) { @Override protected void handlePreferenceChanged(final IChangeEvent event) { if (event.contains(IREnvManager.PREF_QUALIFIER)) { loadREnvironments(); } } }.subscribe(PREF_QUALIFIERS); loadREnvironments(); } private void createControls() { final Composite container = this; container.setLayout(LayoutUtil.createCompositeGrid(3)); if (fEnableNone) { fNoneButton = new Button(container, SWT.RADIO); fNoneButton.setText(RUIMessages.ChooseREnv_None_label); fNoneButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1)); } fWorkbenchDefaultButton = new Button(container, SWT.RADIO); fWorkbenchDefaultButton.setText(RUIMessages.ChooseREnv_WorkbenchDefault_label); fWorkbenchDefaultButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); fWorkbenchLabel = new Text(container, SWT.BORDER | SWT.LEFT | SWT.SINGLE | SWT.READ_ONLY); fWorkbenchLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); LayoutUtil.addGDDummy(container); fSpecificButton = new Button(container, SWT.RADIO); fSpecificButton.setText(RUIMessages.ChooseREnv_Selected_label); fSpecificButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); fSpecificCombo = new Combo(container, SWT.DROP_DOWN | SWT.READ_ONLY); fSpecificCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); fConfigurationButton = new Button(container, SWT.PUSH); fConfigurationButton.setText(RUIMessages.ChooseREnv_Configure_label); fConfigurationButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); if (fEnableNone) { fNoneButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { if (fNoneButton.getSelection()) { fCurrentREnv = null; updateState(false, false); } } }); } fWorkbenchDefaultButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { if (fWorkbenchDefaultButton.getSelection()) { fCurrentREnv = RCore.getREnvManager().getDefault(); updateState(false, false); } } }); fSpecificButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { if (fSpecificButton.getSelection()) { fCurrentREnv = fCurrentSpecified; updateState(false, false); } } }); fSpecificCombo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent event) { final String name = getSpecifiedName(); if (name != null) { fCurrentREnv = RCore.getREnvManager().get(null, name); updateState(false, false); } } }); fConfigurationButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { org.eclipse.ui.dialogs.PreferencesUtil.createPreferenceDialogOn(getShell(), REnvPreferencePage.PREF_PAGE_ID, new String[] { REnvPreferencePage.PREF_PAGE_ID }, null).open(); } }); } private void loadREnvironments() { fInvalidPreference = true; final IREnvManager manager = RCore.getREnvManager(); manager.getReadLock().lock(); try { // Workbench default final IREnv defaultEnv = manager.getDefault(); final List<IREnvConfiguration> list = manager.getConfigurations(); fValidREnvs = getValidREnvs(list); Collections.sort(fValidREnvs, RENV_COMPARATOR); final String[] validNames = new String[fValidREnvs.size()]; for (int i = 0; i < validNames.length; i++) { validNames[i] = fValidREnvs.get(i).getName(); } fWorkbenchLabel.setText(defaultEnv.getName()); if (!list.isEmpty()) { fInvalidPreference = false; } // Specifics fSpecificCombo.setItems(validNames); if (fCurrentSpecified != null) { final boolean current = (fCurrentREnv == fCurrentSpecified); fCurrentSpecified = manager.get(fCurrentSpecified.getId(), fCurrentSpecified.getName()); if (current) { fCurrentREnv = fCurrentSpecified; } } } finally { manager.getReadLock().unlock(); updateState(false, true); } } protected List<IREnv> getValidREnvs(final List<IREnvConfiguration> configurations) { final List<IREnv> list= new ArrayList<>(configurations.size()); for (final IREnvConfiguration rEnvConfig : configurations) { if (isValid(rEnvConfig)) { list.add(rEnvConfig.getReference()); } } return list; } protected boolean isValid(final IREnvConfiguration rEnvConfig) { return (!rEnvConfig.isDeleted()); } public void setSetting(final IREnv rEnv) { fCurrentREnv = rEnv; updateState(true, false); } public String getEncodedSetting() { return fCurrentEncoded; } public void setEncodedSetting(final String encodedSetting) { setSetting(REnvUtil.decode(encodedSetting, RCore.getREnvManager())); } private String getSpecifiedName() { final int idx = fSpecificCombo.getSelectionIndex(); if (idx >= 0) { return fSpecificCombo.getItem(idx); } return null; } public IREnv getSelection() { return fCurrentREnv; } private void updateState(final boolean updateSelection, final boolean force) { final boolean isWorkbench = (fCurrentREnv != null && fCurrentREnv.getId().equals(IREnv.DEFAULT_WORKBENCH_ENV_ID) ); final boolean isSpecific = (fCurrentREnv != null && !isWorkbench); if (updateSelection) { if (fNoneButton != null) { fNoneButton.setSelection(!isWorkbench && !isSpecific); } fWorkbenchDefaultButton.setSelection(isWorkbench); fSpecificButton.setSelection(isSpecific); } fWorkbenchLabel.setEnabled(fWorkbenchDefaultButton.getSelection()); fSpecificCombo.setEnabled(fSpecificButton.getSelection()); if (isSpecific) { fCurrentSpecified = fCurrentREnv; } if (fCurrentSpecified != null) { fSpecificCombo.select(fSpecificCombo.indexOf(fCurrentSpecified.getName())); } else { fSpecificCombo.deselectAll(); } final String oldEncoded = fCurrentEncoded; fCurrentEncoded = REnvUtil.encode(fCurrentREnv); if (!((fCurrentEncoded != null) ? fCurrentEncoded.equals(oldEncoded) : (null == oldEncoded))) { for (final ChangeListener listener : fListeners.toArray()) { listener.settingChanged(this, oldEncoded, fCurrentEncoded, fCurrentREnv); } } else if (force) { checkBindings(); if (fBindings != null) { fBindings.validateTargetToModel(); } } } private void checkBindings() { if (fBindindContexts != null) { final IObservableList bindings = fBindindContexts.getBindings(); for (final Object obj : bindings) { final Binding binding = (Binding) obj; if (binding.getTarget() instanceof CompositeObservable) { if (((CompositeObservable) binding.getTarget()).getComposite() == this) { fBindings = binding; fBindindContexts = null; return; } } } } } public void addChangeListener(final ChangeListener listener) { fListeners.add(listener); } public void removeChangeListener(final ChangeListener listener) { fListeners.remove(listener); } /** * Return a new Observable for the encoded setting of selected REnv. * (So type is String) */ public IObservableValue createObservable(final Realm realm) { return new CompositeObservable(realm); } public ChooseREnvValidator createValidator(final DataBindingContext context) { fBindindContexts = context; return new ChooseREnvValidator(); } }