| /*=============================================================================# |
| # Copyright (c) 2007, 2021 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.internal.r.debug.ui.preferences; |
| |
| import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullElse; |
| |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.databinding.DataBindingContext; |
| import org.eclipse.core.databinding.ValidationStatusProvider; |
| import org.eclipse.core.databinding.observable.IObservable; |
| import org.eclipse.core.databinding.observable.Observables; |
| import org.eclipse.core.databinding.observable.list.IObservableList; |
| import org.eclipse.core.databinding.observable.list.WritableList; |
| import org.eclipse.core.databinding.observable.value.IObservableValue; |
| import org.eclipse.core.databinding.observable.value.WritableValue; |
| import org.eclipse.core.databinding.validation.ValidationStatus; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.jface.databinding.swt.typed.WidgetProperties; |
| import org.eclipse.jface.databinding.viewers.typed.ViewerProperties; |
| import org.eclipse.jface.dialogs.Dialog; |
| import org.eclipse.jface.viewers.ArrayContentProvider; |
| import org.eclipse.jface.viewers.ColumnLabelProvider; |
| import org.eclipse.jface.viewers.ColumnWeightData; |
| import org.eclipse.jface.viewers.ComboViewer; |
| import org.eclipse.jface.viewers.IElementComparer; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.LabelProvider; |
| import org.eclipse.jface.viewers.TableViewer; |
| import org.eclipse.jface.viewers.TableViewerColumn; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.jface.viewers.ViewerComparator; |
| 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.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Group; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Link; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.MenuItem; |
| import org.eclipse.ui.statushandlers.StatusManager; |
| |
| import org.eclipse.statet.jcommons.collections.ImCollections; |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| |
| import org.eclipse.statet.ecommons.databinding.jface.DataBindingSupport; |
| import org.eclipse.statet.ecommons.preferences.core.Preference; |
| import org.eclipse.statet.ecommons.preferences.core.PreferenceAccess; |
| import org.eclipse.statet.ecommons.preferences.core.util.PreferenceUtils; |
| import org.eclipse.statet.ecommons.preferences.ui.ConfigurationBlock; |
| import org.eclipse.statet.ecommons.preferences.ui.ConfigurationBlockPreferencePage; |
| import org.eclipse.statet.ecommons.preferences.ui.ManagedConfigurationBlock; |
| import org.eclipse.statet.ecommons.runtime.core.StatusChangeListener; |
| import org.eclipse.statet.ecommons.ui.SharedMessages; |
| import org.eclipse.statet.ecommons.ui.components.ButtonGroup; |
| import org.eclipse.statet.ecommons.ui.components.DataAdapter; |
| import org.eclipse.statet.ecommons.ui.components.DropDownButton; |
| import org.eclipse.statet.ecommons.ui.util.LayoutUtils; |
| import org.eclipse.statet.ecommons.ui.viewers.ViewerUtils; |
| import org.eclipse.statet.ecommons.ui.viewers.ViewerUtils.TableComposite; |
| |
| import org.eclipse.statet.internal.r.ui.help.IRUIHelpContextIds; |
| import org.eclipse.statet.r.core.RCore; |
| import org.eclipse.statet.r.core.RCorePreferenceNodes; |
| import org.eclipse.statet.r.core.renv.IREnvConfiguration; |
| import org.eclipse.statet.r.core.renv.IREnvManager; |
| import org.eclipse.statet.r.launching.RRunDebugPreferenceConstants; |
| import org.eclipse.statet.r.ui.RUI; |
| import org.eclipse.statet.rj.renv.core.REnv; |
| |
| |
| /** |
| * Preference page for R (Environment) configuration of the workbench. |
| */ |
| public class REnvPreferencePage extends ConfigurationBlockPreferencePage { |
| |
| |
| public static final String PREF_PAGE_ID= "org.eclipse.statet.r.preferencePages.REnvironmentPage"; //$NON-NLS-1$ |
| |
| |
| public REnvPreferencePage() { |
| } |
| |
| |
| @Override |
| protected ConfigurationBlock createConfigurationBlock() throws CoreException { |
| return new REnvConfigurationBlock(null, createStatusChangedListener()); |
| } |
| |
| } |
| |
| |
| class REnvConfigurationBlock extends ManagedConfigurationBlock |
| implements ButtonGroup.IActions<IREnvConfiguration.WorkingCopy> { |
| |
| |
| private final static int ADD_NEW_DEFAULT= ButtonGroup.ADD_NEW; |
| private final static int ADD_NEW_REMOTE= ButtonGroup.ADD_NEW | (0x1 << 8); |
| |
| @NonNullByDefault |
| private static String trimUri(final String s) { |
| try { |
| final URI uri= new URI(s); |
| final URI uiUri= new URI(uri.getScheme(), null, |
| uri.getHost(), uri.getPort(), |
| uri.getPath(), null, null ); |
| return uiUri.toString(); |
| } |
| catch (final URISyntaxException e) { |
| return s; |
| } |
| } |
| |
| |
| private TableViewer listViewer; |
| private ButtonGroup<IREnvConfiguration.WorkingCopy> listButtons; |
| |
| private final IObservableList<IREnvConfiguration.WorkingCopy> envList= new WritableList<>(); |
| private final IObservableValue<IREnvConfiguration.WorkingCopy> envDefault= new WritableValue<>(); |
| private final IObservableValue<IStatus> envListStatus= new WritableValue<>(); |
| |
| private ComboViewer indexConsoleViewer; |
| private Button networkEclipseControl; |
| |
| |
| protected REnvConfigurationBlock(final IProject project, final StatusChangeListener statusListener) { |
| super(project, statusListener); |
| } |
| |
| |
| @Override |
| protected String getHelpContext() { |
| return IRUIHelpContextIds.R_ENV; |
| } |
| |
| @Override |
| protected void createBlockArea(final Composite pageComposite) { |
| final Map<Preference<?>, String> prefs= new HashMap<>(); |
| |
| prefs.put(RRunDebugPreferenceConstants.PREF_RENV_CHECK_UPDATE, null); |
| |
| setupPreferenceManager(prefs); |
| |
| final Label label= new Label(pageComposite, SWT.LEFT); |
| label.setText(Messages.REnv_REnvList_label); |
| label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); |
| |
| { // Table area |
| final Composite composite= new Composite(pageComposite, SWT.NONE); |
| composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| composite.setLayout(LayoutUtils.newCompositeGrid(2)); |
| |
| final Composite table= createTable(composite); |
| { final GridData gd= new GridData(SWT.FILL, SWT.FILL, true, true); |
| gd.heightHint= LayoutUtils.hintHeight(this.listViewer.getTable(), 12, false); |
| table.setLayoutData(gd); |
| } |
| |
| this.listButtons= new ButtonGroup<>(composite, this, false); |
| this.listButtons.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, true)); |
| final SelectionListener addDefaultListener= new SelectionAdapter() { |
| @Override |
| public void widgetSelected(final SelectionEvent e) { |
| REnvConfigurationBlock.this.listButtons.editElement(ADD_NEW_DEFAULT, null); |
| } |
| }; |
| final DropDownButton addButton= new DropDownButton(this.listButtons); |
| final Menu addMenu= addButton.getDropDownMenu(); |
| { final MenuItem menuItem= new MenuItem(addMenu, SWT.PUSH); |
| menuItem.setText(Messages.REnv_Add_Local_label); |
| menuItem.addSelectionListener(addDefaultListener); |
| } |
| { final MenuItem menuItem= new MenuItem(addMenu, SWT.PUSH); |
| menuItem.setText(Messages.REnv_Add_Remote_label); |
| menuItem.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(final SelectionEvent e) { |
| REnvConfigurationBlock.this.listButtons.editElement(ADD_NEW_REMOTE, null); |
| } |
| }); |
| } |
| addButton.addSelectionListener(addDefaultListener); |
| addButton.setText(SharedMessages.CollectionEditing_AddItem_label + "..."); |
| addButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); |
| this.listButtons.addCopyButton(null); |
| this.listButtons.addEditButton(null); |
| this.listButtons.addDeleteButton(null); |
| this.listButtons.addSeparator(); |
| this.listButtons.addDefaultButton(null); |
| |
| this.listButtons.connectTo(this.listViewer, new DataAdapter.ListAdapter<IREnvConfiguration.WorkingCopy>( |
| this.envList, this.envDefault ) { |
| @Override |
| public boolean isDeleteAllowed(final Object element) { |
| final IREnvConfiguration config= (IREnvConfiguration) element; |
| return config.isEditable(); |
| } |
| }); |
| this.listViewer.setComparer(new IElementComparer() { |
| @Override |
| public int hashCode(final Object element) { |
| if (element instanceof IREnvConfiguration) { |
| return ((IREnvConfiguration) element).getREnv().hashCode(); |
| } |
| return element.hashCode(); |
| } |
| |
| @Override |
| public boolean equals(final Object a, final Object b) { |
| if (a instanceof IREnvConfiguration && b instanceof IREnvConfiguration) { |
| return ((IREnvConfiguration) a).getREnv().equals( |
| ((IREnvConfiguration) b).getREnv()); |
| } |
| return a.equals(b); |
| } |
| }); |
| |
| this.listViewer.setInput(this.envList); |
| ViewerUtils.scheduleStandardSelection(this.listViewer); |
| } |
| loadValues(PreferenceUtils.getInstancePrefs()); |
| |
| final Composite indexOptions= createIndexOptions(pageComposite); |
| indexOptions.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); |
| |
| final Composite networkOptions= createNetworkOptions(pageComposite); |
| networkOptions.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); |
| |
| initBindings(); |
| updateStatus(); |
| |
| final DataBindingContext dbc= getDataBinding().getContext(); |
| dbc.addValidationStatusProvider(new ValidationStatusProvider() { |
| @Override |
| public IObservableValue<IStatus> getValidationStatus() { |
| return REnvConfigurationBlock.this.envListStatus; |
| } |
| @Override |
| public IObservableList<IObservable> getModels() { |
| return Observables.staticObservableList(dbc.getValidationRealm(), |
| Collections.emptyList()); |
| } |
| @Override |
| public IObservableList<IObservable> getTargets() { |
| return Observables.staticObservableList(dbc.getValidationRealm(), |
| Collections.emptyList()); |
| } |
| }); |
| |
| updateControls(); |
| this.listButtons.refresh(); |
| } |
| |
| @Override |
| public IREnvConfiguration.WorkingCopy edit(final int command, |
| final IREnvConfiguration.WorkingCopy config, final Object parent) { |
| final boolean newConfig= ((command & ButtonGroup.ADD_ANY) != 0); |
| final IREnvConfiguration.WorkingCopy editConfig; |
| if (newConfig) { |
| if (config != null) { // copy |
| editConfig= RCore.getREnvManager().newConfiguration(config.getType()); |
| editConfig.load(config); |
| } |
| else { // add |
| if (command == ADD_NEW_REMOTE) { |
| editConfig= RCore.getREnvManager() |
| .newConfiguration(IREnvConfiguration.USER_REMOTE_TYPE); |
| } |
| else { |
| editConfig= RCore.getREnvManager() |
| .newConfiguration(IREnvConfiguration.USER_LOCAL_TYPE); |
| } |
| } |
| } |
| else { |
| editConfig= config.createWorkingCopy(); |
| } |
| if (doEdit(editConfig, newConfig)) { |
| if (newConfig) { |
| return editConfig; |
| } |
| else { |
| config.load(editConfig); |
| return config; |
| } |
| } |
| return null; |
| } |
| |
| private boolean doEdit(final IREnvConfiguration.WorkingCopy config, final boolean newConfig) { |
| final List<IREnvConfiguration> existingConfigs= new ArrayList<>(this.envList); |
| if (!newConfig) { |
| for (final Iterator<IREnvConfiguration> iter= existingConfigs.iterator(); iter.hasNext();) { |
| final IREnvConfiguration existing= iter.next(); |
| if (existing.getREnv() == config.getREnv()) { |
| iter.remove(); |
| break; |
| } |
| } |
| } |
| Dialog dialog; |
| if (config.isLocal()) { |
| dialog= new LocalREnvConfigDialog(getShell(), |
| config, newConfig, existingConfigs); |
| } |
| else if (config.isRemote()) { |
| dialog= new RemoteREnvConfigDialog(getShell(), |
| config, newConfig, existingConfigs); |
| } |
| else { |
| return false; |
| } |
| return (dialog.open() == Dialog.OK); |
| } |
| |
| @Override |
| public void updateState(final IStructuredSelection selection) { |
| REnvConfigurationBlock.this.updateStatus(); |
| } |
| |
| private Composite createTable(final Composite parent) { |
| final TableComposite composite= new ViewerUtils.TableComposite(parent, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION | SWT.V_SCROLL); |
| this.listViewer= composite.viewer; |
| composite.table.setHeaderVisible(true); |
| composite.table.setLinesVisible(true); |
| |
| { final TableViewerColumn column= new TableViewerColumn(composite.viewer, SWT.NONE); |
| composite.layout.setColumnData(column.getColumn(), new ColumnWeightData(1)); |
| column.getColumn().setText(Messages.REnv_NameColumn_name); |
| column.setLabelProvider(new REnvLabelProvider(this.envDefault)); |
| } |
| |
| { final TableViewerColumn column= new TableViewerColumn(composite.viewer, SWT.NONE); |
| composite.layout.setColumnData(column.getColumn(), new ColumnWeightData(1)); |
| column.getColumn().setText(Messages.REnv_LocationColumn_name); |
| column.setLabelProvider(new ColumnLabelProvider() { |
| @Override |
| public String getText(final Object element) { |
| final IREnvConfiguration config= (IREnvConfiguration) element; |
| if (config.getType() == IREnvConfiguration.USER_LOCAL_TYPE) { |
| return nonNullElse(config.getRHomeDirectory(), ""); //$NON-NLS-1$ |
| } |
| if (config.getType() == IREnvConfiguration.EPLUGIN_LOCAL_TYPE) { |
| return "<supplied>"; |
| } |
| if (config.getType() == IREnvConfiguration.USER_REMOTE_TYPE) { |
| final String server= config.getStateSharedServer(); |
| return (server != null) ? trimUri(server) : ""; //$NON-NLS-1$ |
| } |
| return ""; //$NON-NLS-1$ |
| } |
| }); |
| } |
| |
| composite.viewer.setContentProvider(new ArrayContentProvider()); |
| // Sorter |
| composite.viewer.setComparator(new ViewerComparator() { |
| @Override |
| public int compare(final Viewer viewer, final Object e1, final Object e2) { |
| return getComparator().compare(((IREnvConfiguration) e1).getName(), ((IREnvConfiguration) e2).getName()); |
| } |
| }); |
| |
| return composite; |
| } |
| |
| private Composite createIndexOptions(final Composite parent) { |
| final Group composite= new Group(parent, SWT.NONE); |
| composite.setLayout(LayoutUtils.newGroupGrid(2)); |
| composite.setText(Messages.REnv_Index_label); |
| |
| final Label label= new Label(composite, SWT.NONE); |
| label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); |
| label.setText(Messages.REnv_Update_Console_label); |
| |
| this.indexConsoleViewer= new ComboViewer(composite, SWT.DROP_DOWN | SWT.READ_ONLY); |
| this.indexConsoleViewer.getControl().setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false)); |
| |
| this.indexConsoleViewer.setLabelProvider(new LabelProvider() { |
| @Override |
| public String getText(final Object element) { |
| if (element.equals(RRunDebugPreferenceConstants.AUTO)) { |
| return Messages.REnv_Update_Console_Auto_label; |
| } |
| if (element.equals(RRunDebugPreferenceConstants.ASK)) { |
| return Messages.REnv_Update_Console_Ask_label; |
| } |
| if (element.equals(RRunDebugPreferenceConstants.DISABLED)) { |
| return Messages.REnv_Update_Console_Disabled_label; |
| } |
| return ""; //$NON-NLS-1$ |
| } |
| }); |
| this.indexConsoleViewer.setContentProvider(new ArrayContentProvider()); |
| this.indexConsoleViewer.setInput(new String[] { |
| RRunDebugPreferenceConstants.AUTO, |
| RRunDebugPreferenceConstants.ASK, |
| RRunDebugPreferenceConstants.DISABLED, |
| }); |
| |
| return composite; |
| } |
| |
| private Composite createNetworkOptions(final Composite parent) { |
| final Group composite= new Group(parent, SWT.NONE); |
| composite.setLayout(LayoutUtils.newGroupGrid(2)); |
| composite.setText(Messages.REnv_Network_label); |
| |
| { final Composite line= new Composite(composite, SWT.NONE); |
| line.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); |
| final GridLayout layout= LayoutUtils.newCompositeGrid(2); |
| layout.horizontalSpacing= 0; |
| line.setLayout(layout); |
| |
| this.networkEclipseControl= new Button(line, SWT.CHECK); |
| final int idx= Messages.REnv_Network_UseEclipse_label.indexOf("<a"); |
| this.networkEclipseControl.setText(Messages.REnv_Network_UseEclipse_label.substring(0, idx).trim()); |
| this.networkEclipseControl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); |
| final Link link= addLinkControl(line, Messages.REnv_Network_UseEclipse_label.substring(idx)); |
| link.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); |
| } |
| return composite; |
| } |
| |
| @Override |
| protected void addBindings(final DataBindingSupport db) { |
| db.getContext().bindValue( |
| ViewerProperties.singleSelection(String.class) |
| .observe(this.indexConsoleViewer), |
| createObservable(RRunDebugPreferenceConstants.PREF_RENV_CHECK_UPDATE) ); |
| db.getContext().bindValue( |
| WidgetProperties.buttonSelection() |
| .observe(this.networkEclipseControl), |
| createObservable(RCorePreferenceNodes.PREF_RENV_NETWORK_USE_ECLIPSE) ); |
| } |
| |
| |
| @Override |
| public boolean performOk(final int flags) { |
| boolean ok= super.performOk(flags); |
| if (this.listButtons.getDataAdapter().isDirty()) { |
| ok&= saveValues((flags & SAVE_STORE) != 0); |
| } |
| return ok; |
| } |
| |
| |
| private void updateStatus() { |
| this.envListStatus.setValue((this.envDefault.getValue() == null) ? |
| ValidationStatus.warning(Messages.REnv_warning_NoDefaultConfiguration_message) : |
| ValidationStatus.ok() ); |
| } |
| |
| private boolean saveValues(final boolean saveStore) { |
| try { |
| final IREnvConfiguration defaultREnv= this.envDefault.getValue(); |
| RCore.getREnvManager().set( |
| ImCollections.toList(this.envList), |
| (defaultREnv != null) ? defaultREnv.getREnv().getId() : null ); |
| return true; |
| } |
| catch (final CoreException e) { |
| StatusManager.getManager().handle(new Status(IStatus.ERROR, RUI.BUNDLE_ID, |
| -1, Messages.REnv_error_Saving_message, e), |
| StatusManager.LOG | StatusManager.SHOW); |
| return false; |
| } |
| } |
| |
| private void loadValues(final PreferenceAccess prefs) { |
| this.envList.clear(); |
| this.envDefault.setValue(null); |
| |
| final IREnvManager manager= RCore.getREnvManager(); |
| final REnv defaultEnv= manager.getDefault().resolve(); |
| final List<IREnvConfiguration> rEnvConfigs= manager.getConfigurations(); |
| for (final IREnvConfiguration rEnvConfig : rEnvConfigs) { |
| final IREnvConfiguration.WorkingCopy config= rEnvConfig.createWorkingCopy(); |
| this.envList.add(config); |
| if (config.getREnv() == defaultEnv) { |
| this.envDefault.setValue(config); |
| } |
| } |
| } |
| |
| } |