| /*=============================================================================# |
| # Copyright (c) 2007, 2019 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.rj.renv.core.REnvConfiguration.R_HOME_DIRECTORY_VAR_STRING; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.lang.reflect.InvocationTargetException; |
| import java.nio.file.Paths; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.regex.Pattern; |
| |
| import com.ibm.icu.text.Collator; |
| |
| import org.eclipse.core.databinding.Binding; |
| import org.eclipse.core.databinding.UpdateValueStrategy; |
| import org.eclipse.core.databinding.beans.typed.BeanProperties; |
| import org.eclipse.core.databinding.observable.value.IValueChangeListener; |
| import org.eclipse.core.databinding.observable.value.ValueChangeEvent; |
| import org.eclipse.core.databinding.validation.ValidationStatus; |
| import org.eclipse.core.filesystem.EFS; |
| import org.eclipse.core.filesystem.IFileStore; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.variables.IStringVariableManager; |
| import org.eclipse.core.variables.VariablesPlugin; |
| import org.eclipse.jface.databinding.swt.typed.WidgetProperties; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.operation.IRunnableWithProgress; |
| import org.eclipse.jface.viewers.CellEditor; |
| import org.eclipse.jface.viewers.ColumnWeightData; |
| import org.eclipse.jface.viewers.EditingSupport; |
| import org.eclipse.jface.viewers.ITreeContentProvider; |
| import org.eclipse.jface.viewers.TreeViewer; |
| import org.eclipse.jface.viewers.TreeViewerColumn; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.jface.viewers.ViewerCell; |
| import org.eclipse.osgi.util.NLS; |
| 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.Control; |
| import org.eclipse.swt.widgets.Group; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.MenuItem; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.swt.widgets.Text; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.statushandlers.StatusManager; |
| |
| import org.eclipse.statet.jcommons.collections.ImCollections; |
| import org.eclipse.statet.jcommons.collections.ImList; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| import org.eclipse.statet.jcommons.status.StatusException; |
| |
| import org.eclipse.statet.ecommons.databinding.jface.DataBindingSupport; |
| import org.eclipse.statet.ecommons.debug.core.util.LaunchUtils; |
| import org.eclipse.statet.ecommons.debug.ui.util.ProcessOutputCollector; |
| import org.eclipse.statet.ecommons.io.FileUtil; |
| import org.eclipse.statet.ecommons.runtime.core.util.StatusUtils; |
| import org.eclipse.statet.ecommons.ui.components.ButtonGroup; |
| import org.eclipse.statet.ecommons.ui.components.DataAdapter; |
| import org.eclipse.statet.ecommons.ui.components.ExtensibleTextCellEditor; |
| import org.eclipse.statet.ecommons.ui.dialogs.ExtStatusDialog; |
| import org.eclipse.statet.ecommons.ui.util.LayoutUtils; |
| import org.eclipse.statet.ecommons.ui.util.MessageUtils; |
| import org.eclipse.statet.ecommons.ui.util.VariableFilterUtils; |
| import org.eclipse.statet.ecommons.ui.viewers.ViewerUtils; |
| import org.eclipse.statet.ecommons.ui.viewers.ViewerUtils.TreeComposite; |
| import org.eclipse.statet.ecommons.ui.workbench.ResourceInputComposite; |
| |
| import org.eclipse.statet.internal.r.ui.RUIPlugin; |
| import org.eclipse.statet.internal.r.ui.help.IRUIHelpContextIds; |
| import org.eclipse.statet.r.core.RUtil; |
| import org.eclipse.statet.r.core.renv.IREnvConfiguration; |
| import org.eclipse.statet.r.core.renv.IREnvConfiguration.Exec; |
| import org.eclipse.statet.r.core.renv.RLibGroupWorkingCopy; |
| import org.eclipse.statet.r.ui.REnvLabelProvider; |
| import org.eclipse.statet.r.ui.RUI; |
| import org.eclipse.statet.rj.renv.core.BasicREnvConfiguration; |
| import org.eclipse.statet.rj.renv.core.DefaultLocalConfigurator; |
| import org.eclipse.statet.rj.renv.core.RLibGroup; |
| import org.eclipse.statet.rj.renv.core.RLibLocation; |
| |
| |
| /** |
| * Dialog for a local standard {@link IREnvConfiguration} (<code>user-local</code>) |
| */ |
| public class LocalREnvConfigDialog extends ExtStatusDialog { |
| |
| |
| private static final String DETECT_START= "_R-Path-And-Library-Configuration_"; //$NON-NLS-1$ |
| private static final String DETECT_COMMAND= "cat('"+DETECT_START+"'," //$NON-NLS-1$ //$NON-NLS-2$ |
| + "Sys.getenv(\'R_HOME\')," //$NON-NLS-1$ |
| + "Sys.getenv(\'R_ARCH\')," //$NON-NLS-1$ |
| + "paste(.Library,collapse=.Platform$path.sep)," //$NON-NLS-1$ |
| + "paste(.Library.site,collapse=.Platform$path.sep)," //$NON-NLS-1$ |
| + "Sys.getenv('R_LIBS')," //$NON-NLS-1$ |
| + "Sys.getenv('R_LIBS_USER')," //$NON-NLS-1$ |
| + "R.home('doc')," //$NON-NLS-1$ |
| + "R.home('share')," //$NON-NLS-1$ |
| + "R.home('include')," //$NON-NLS-1$ |
| + "R.version$arch," //$NON-NLS-1$ |
| + ".Platform$OS.type," //$NON-NLS-1$ |
| + "sep=intToUtf8(0x0AL));"; //$NON-NLS-1$ |
| |
| private static final int DETECT_LENGTH= 12; |
| private static final int DETECT_R_HOME= 1; |
| private static final int DETECT_R_ARCHVAR= 2; |
| private static final int DETECT_R_DEFAULT= 3; |
| private static final int DETECT_R_SITE= 4; |
| private static final int DETECT_R_OTHER= 5; |
| private static final int DETECT_R_USER= 6; |
| private static final int DETECT_R_DOC_DIR= 7; |
| private static final int DETECT_R_SHARE_DIR= 8; |
| private static final int DETECT_R_INCLUDE_DIR= 9; |
| private static final int DETECT_R_ARCH= 10; |
| private static final int DETECT_R_OS= 11; |
| private static final Pattern DETECT_ITEM_PATTERN= RUtil.LINE_SEPARATOR_PATTERN; |
| private static final Pattern DETECT_PATH_PATTERN= Pattern.compile(File.pathSeparator, Pattern.LITERAL); |
| |
| |
| private class RHomeComposite extends ResourceInputComposite { |
| |
| public RHomeComposite(final Composite parent) { |
| super (parent, |
| ResourceInputComposite.STYLE_TEXT, |
| ResourceInputComposite.MODE_DIRECTORY | ResourceInputComposite.MODE_OPEN, |
| Messages.REnv_Detail_Location_label); |
| setShowInsertVariable(true, VariableFilterUtils.DEFAULT_NON_ITERACTIVE_FILTERS, null); |
| } |
| |
| @Override |
| protected void fillMenu(final Menu menu) { |
| super.fillMenu(menu); |
| |
| final MenuItem item= new MenuItem(menu, SWT.PUSH); |
| item.setText(Messages.REnv_Detail_Location_FindAuto_label); |
| item.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(final SelectionEvent e) { |
| final String[] rhome= searchRHOME(); |
| if (rhome != null) { |
| setText(rhome[0]); |
| updateArchs(false); |
| final String current= LocalREnvConfigDialog.this.nameControl.getText().trim(); |
| if ((current.isEmpty() || current.equals("R")) && rhome[1] != null) { //$NON-NLS-1$ |
| LocalREnvConfigDialog.this.nameControl.setText(rhome[1]); |
| } |
| } |
| else { |
| final String name= Messages.REnv_Detail_Location_label; |
| MessageDialog.openInformation(getShell(), |
| MessageUtils.removeMnemonics(name), |
| NLS.bind(Messages.REnv_Detail_Location_FindAuto_Failed_message, name)); |
| } |
| getTextControl().setFocus(); |
| } |
| }); |
| |
| } |
| |
| } |
| |
| private static class RLibraryContainer { |
| |
| |
| private final RLibGroupWorkingCopy parent; |
| |
| private RLibLocation library; |
| |
| |
| RLibraryContainer(final RLibGroupWorkingCopy parent, final RLibLocation library) { |
| this.parent= parent; |
| this.library= library; |
| } |
| |
| |
| public boolean set(final String directory) { |
| if (!this.library.getDirectory().equals(directory)) { |
| this.library= this.parent.setLibrary(this.library, directory); |
| return true; |
| } |
| return false; |
| } |
| |
| |
| @Override |
| public int hashCode() { |
| return this.library.hashCode(); |
| } |
| |
| @Override |
| public boolean equals(final @Nullable Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (obj instanceof RLibraryContainer) { |
| final RLibraryContainer other= (RLibraryContainer) obj; |
| return (this.library == other.library); |
| } |
| return false; |
| } |
| } |
| |
| |
| private final IREnvConfiguration.WorkingCopy configModel; |
| private final boolean isNewConfig; |
| private final Set<String> existingNames; |
| |
| private Text nameControl; |
| private ResourceInputComposite rHomeControl; |
| |
| private Button loadButton; |
| |
| private Combo rArchControl; |
| |
| private TreeViewer rLibrariesViewer; |
| private ButtonGroup<RLibLocation> rLibrariesButtons; |
| |
| private ResourceInputComposite rDocDirectoryControl; |
| private ResourceInputComposite rShareDirectoryControl; |
| private ResourceInputComposite rIncludeDirectoryControl; |
| |
| |
| public LocalREnvConfigDialog(final Shell parent, |
| final IREnvConfiguration.WorkingCopy config, final boolean isNewConfig, |
| final Collection<IREnvConfiguration> existingConfigs) { |
| super(parent, WITH_RUNNABLE_CONTEXT | ((isNewConfig) ? WITH_DATABINDING_CONTEXT : |
| (WITH_DATABINDING_CONTEXT | SHOW_INITIAL_STATUS)) ); |
| |
| this.configModel= config; |
| this.isNewConfig= isNewConfig; |
| this.existingNames= new HashSet<>(); |
| for (final IREnvConfiguration ec : existingConfigs) { |
| this.existingNames.add(ec.getName()); |
| } |
| setTitle(this.isNewConfig ? |
| Messages.REnv_Detail_AddDialog_title : |
| Messages.REnv_Detail_Edit_Dialog_title ); |
| } |
| |
| |
| @Override |
| public void create() { |
| super.create(); |
| |
| PlatformUI.getWorkbench().getHelpSystem().setHelp(getShell(), IRUIHelpContextIds.R_ENV); |
| } |
| |
| @Override |
| protected Control createDialogArea(final Composite parent) { |
| final Composite area= new Composite(parent, SWT.NONE); |
| area.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| area.setLayout(LayoutUtils.newDialogGrid(2)); |
| |
| { // Name: |
| final Label label= new Label(area, SWT.LEFT); |
| label.setText(Messages.REnv_Detail_Name_label+':'); |
| label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); |
| |
| final Text text= new Text(area, SWT.BORDER); |
| final GridData gd= new GridData(SWT.FILL, SWT.CENTER, true, false); |
| gd.widthHint= LayoutUtils.hintWidth(text, 60); |
| text.setLayoutData(gd); |
| text.setEditable(this.configModel.isEditable()); |
| this.nameControl= text; |
| } |
| |
| if (this.configModel.isEditable()) { |
| // Location: |
| final Label label= new Label(area, SWT.LEFT); |
| label.setText(Messages.REnv_Detail_Location_label+':'); |
| label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); |
| |
| this.rHomeControl= new RHomeComposite(area); |
| this.rHomeControl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); |
| } |
| |
| LayoutUtils.addSmallFiller(area, false); |
| |
| { // Architecture / Bits: |
| final Label label= new Label(area, SWT.LEFT); |
| label.setText(Messages.REnv_Detail_Arch_label+':'); |
| label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); |
| |
| final Composite composite= new Composite(area, SWT.NONE); |
| composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); |
| composite.setLayout(LayoutUtils.newCompositeGrid(3)); |
| |
| { this.rArchControl= new Combo(composite, SWT.DROP_DOWN); |
| final GridData gd= new GridData(SWT.FILL, SWT.FILL, false, false); |
| gd.widthHint= LayoutUtils.hintWidth(this.rArchControl, 8); |
| this.rArchControl.setLayoutData(gd); |
| this.rArchControl.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(final SelectionEvent e) { |
| final int selectionIdx; |
| if (!LocalREnvConfigDialog.this.rArchControl.getListVisible() |
| && (selectionIdx= LocalREnvConfigDialog.this.rArchControl.getSelectionIndex()) >= 0) { |
| final String item= LocalREnvConfigDialog.this.rArchControl.getItem(selectionIdx); |
| } |
| } |
| }); |
| this.rArchControl.setEnabled(this.configModel.isEditable()); |
| } |
| |
| if (this.configModel.isEditable()) { |
| this.loadButton= new Button(composite, SWT.PUSH); |
| this.loadButton.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, true, false, 2, 1)); |
| this.loadButton.setText(Messages.REnv_Detail_DetectSettings_label); |
| this.loadButton.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(final SelectionEvent e) { |
| detectSettings(); |
| } |
| }); |
| } |
| else { |
| LayoutUtils.addGDDummy(composite, true, 2); |
| } |
| } |
| |
| { // Libraries: |
| final Label label= new Label(area, SWT.LEFT); |
| label.setText(Messages.REnv_Detail_Libraries_label+":"); //$NON-NLS-1$ |
| label.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false)); |
| |
| final Composite composite= new Composite(area, SWT.NONE); |
| composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| composite.setLayout(LayoutUtils.newCompositeGrid(2)); |
| |
| final TreeComposite treeComposite= new ViewerUtils.TreeComposite(composite, SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION); |
| this.rLibrariesViewer= treeComposite.viewer; |
| final GridData gd= new GridData(SWT.FILL, SWT.FILL, true, true); |
| gd.widthHint= LayoutUtils.hintWidth(this.nameControl, 80); |
| gd.heightHint= LayoutUtils.hintHeight(treeComposite.tree, 10); |
| treeComposite.setLayoutData(gd); |
| treeComposite.viewer.setContentProvider(new ITreeContentProvider() { |
| @Override |
| public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) { |
| } |
| @Override |
| public void dispose() { |
| } |
| @Override |
| public Object[] getElements(final Object inputElement) { |
| return LocalREnvConfigDialog.this.configModel.getRLibGroups().toArray(); |
| } |
| @Override |
| public Object getParent(final Object element) { |
| if (element instanceof RLibraryContainer) { |
| return ((RLibraryContainer) element).parent; |
| } |
| return null; |
| } |
| @Override |
| public boolean hasChildren(final Object element) { |
| if (element instanceof RLibGroupWorkingCopy) { |
| return !((RLibGroupWorkingCopy) element).getLibLocations().isEmpty(); |
| } |
| return false; |
| } |
| @Override |
| public Object[] getChildren(final Object parentElement) { |
| if (parentElement instanceof RLibGroupWorkingCopy) { |
| final RLibGroupWorkingCopy group= (RLibGroupWorkingCopy) parentElement; |
| final List<? extends RLibLocation> libs= group.getLibLocations(); |
| final RLibraryContainer[] array= new RLibraryContainer[libs.size()]; |
| for (int i= 0; i < libs.size(); i++) { |
| array[i]= new RLibraryContainer(group, libs.get(i)); |
| } |
| return array; |
| } |
| return null; |
| } |
| }); |
| final TreeViewerColumn column= treeComposite.addColumn(SWT.LEFT, new ColumnWeightData(100)); |
| column.setLabelProvider(new REnvLabelProvider() { |
| @Override |
| public void update(final ViewerCell cell) { |
| final Object element= cell.getElement(); |
| if (element instanceof RLibraryContainer) { |
| final RLibLocation lib= ((RLibraryContainer) element).library; |
| cell.setImage(RUI.getImage(RUI.IMG_OBJ_LIBRARY_LOCATION)); |
| if (lib.getSource() != RLibLocation.USER && lib.getLabel() != null) { |
| cell.setText(lib.getLabel()); |
| } |
| else { |
| cell.setText(lib.getDirectory()); |
| } |
| finishUpdate(cell); |
| return; |
| } |
| super.update(cell); |
| } |
| }); |
| column.setEditingSupport(new EditingSupport(treeComposite.viewer) { |
| @Override |
| protected boolean canEdit(final Object element) { |
| if (element instanceof RLibraryContainer) { |
| final RLibraryContainer container= ((RLibraryContainer) element); |
| return (container.library.getSource() == RLibLocation.USER); |
| } |
| return false; |
| } |
| @Override |
| protected void setValue(final Object element, final Object value) { |
| final RLibraryContainer container= (RLibraryContainer) element; |
| if (container.set((String) value)) { |
| getViewer().refresh(container, true); |
| getDataBinding().updateStatus(); |
| } |
| } |
| @Override |
| protected Object getValue(final Object element) { |
| final RLibraryContainer container= (RLibraryContainer) element; |
| return container.library.getDirectory(); |
| } |
| @Override |
| protected CellEditor getCellEditor(final Object element) { |
| return new ExtensibleTextCellEditor(treeComposite.tree) { |
| @Override |
| protected Control createCustomControl(final Composite parent) { |
| final ResourceInputComposite chooseResourceComposite= new ResourceInputComposite(parent, |
| ResourceInputComposite.STYLE_TEXT, |
| ResourceInputComposite.MODE_DIRECTORY | ResourceInputComposite.MODE_OPEN, |
| Messages.REnv_Detail_LibraryLocation_label) { |
| @Override |
| protected void beforeMenuAction() { |
| getFocusGroup().discontinueTracking(); |
| } |
| @Override |
| protected void afterMenuAction() { |
| getFocusGroup().continueTracking(); |
| } |
| }; |
| chooseResourceComposite.setShowInsertVariable(true, |
| VariableFilterUtils.DEFAULT_NON_ITERACTIVE_FILTERS, null); |
| this.text= (Text) chooseResourceComposite.getTextControl(); |
| return chooseResourceComposite; |
| } |
| }; |
| } |
| }); |
| treeComposite.viewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS); |
| treeComposite.viewer.setInput(this.configModel); |
| ViewerUtils.installDefaultEditBehaviour(treeComposite.viewer); |
| ViewerUtils.scheduleStandardSelection(treeComposite.viewer); |
| |
| this.rLibrariesButtons= new ButtonGroup<RLibLocation>(composite) { |
| @Override |
| protected RLibLocation edit1(final RLibLocation item, final boolean newItem, final Object parent) { |
| if (newItem) { |
| return ((RLibGroupWorkingCopy) parent).newLibrary(""); //$NON-NLS-1$ |
| } |
| return item; |
| } |
| @Override |
| public void updateState() { |
| super.updateState(); |
| if (getDataAdapter().isDirty()) { |
| getDataBinding().updateStatus(); |
| } |
| } |
| }; |
| this.rLibrariesButtons.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true)); |
| this.rLibrariesButtons.addAddButton(null); |
| this.rLibrariesButtons.addDeleteButton(null); |
| // this.rLibrariesButtons.addSeparator(); |
| // this.rLibrariesButtons.addUpButton(); |
| // this.rLibrariesButtons.addDownButton(); |
| |
| final DataAdapter<RLibLocation> adapter= new DataAdapter.TreeAdapter<RLibLocation>( |
| (ITreeContentProvider) this.rLibrariesViewer.getContentProvider(), null ) { |
| private RLibGroupWorkingCopy getGroup(final Object element) { |
| if (element instanceof RLibGroupWorkingCopy) { |
| return (RLibGroupWorkingCopy) element; |
| } |
| else { |
| return ((RLibraryContainer) element).parent; |
| } |
| } |
| @Override |
| public RLibLocation getModelItem(final Object element) { |
| if (element instanceof RLibraryContainer) { |
| return ((RLibraryContainer) element).library; |
| } |
| return (RLibLocation) element; |
| } |
| @Override |
| public Object getViewerElement(final RLibLocation item, final Object parent) { |
| return new RLibraryContainer((RLibGroupWorkingCopy) parent, item); |
| } |
| @Override |
| public boolean isAddAllowed(final Object element) { |
| return !getGroup(element).getId().equals(RLibGroup.R_DEFAULT); |
| } |
| @Override |
| public boolean isModifyAllowed(final Object element) { |
| return ( element instanceof RLibraryContainer |
| && ((RLibraryContainer) element).library.getSource() == RLibLocation.USER ); |
| } |
| @Override |
| public Object getAddParent(final Object element) { |
| return getGroup(element); |
| } |
| @Override |
| public List<? extends RLibLocation> getContainerFor(final Object element) { |
| if (element instanceof RLibGroup) { |
| return ((RLibGroup) element).getLibLocations(); |
| } |
| else { |
| return ((RLibraryContainer) element).parent.getLibLocations(); |
| } |
| } |
| }; |
| this.rLibrariesButtons.connectTo(this.rLibrariesViewer, adapter); |
| } |
| |
| if (this.configModel.isEditable()) { |
| final Composite group= createInstallDirGroup(area); |
| if (group != null) { |
| group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); |
| } |
| } |
| |
| LayoutUtils.addSmallFiller(area, true); |
| |
| applyDialogFont(area); |
| |
| return area; |
| } |
| |
| private Composite createInstallDirGroup(final Composite parent) { |
| final Group composite= new Group(parent, SWT.NONE); |
| composite.setLayout(LayoutUtils.newGroupGrid(2)); |
| composite.setText("Advanced - Installation locations:"); |
| { final Label label= new Label(composite, SWT.NONE); |
| label.setText("Documentation ('R_DOC_DIR'):"); |
| label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); |
| |
| final ResourceInputComposite text= new ResourceInputComposite(composite, ResourceInputComposite.STYLE_TEXT, |
| (ResourceInputComposite.MODE_DIRECTORY | ResourceInputComposite.MODE_OPEN), "R_DOC_DIR"); //$NON-NLS-1$ |
| text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); |
| text.setShowInsertVariable(true, VariableFilterUtils.DEFAULT_NON_ITERACTIVE_FILTERS, null); |
| this.rDocDirectoryControl= text; |
| } |
| { final Label label= new Label(composite, SWT.NONE); |
| label.setText("Shared files ('R_SHARE_DIR'):"); |
| label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); |
| |
| final ResourceInputComposite text= new ResourceInputComposite(composite, ResourceInputComposite.STYLE_TEXT, |
| (ResourceInputComposite.MODE_DIRECTORY | ResourceInputComposite.MODE_OPEN), "R_SHARE_DIR"); //$NON-NLS-1$ |
| text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); |
| text.setShowInsertVariable(true, VariableFilterUtils.DEFAULT_NON_ITERACTIVE_FILTERS, null); |
| this.rShareDirectoryControl= text; |
| } |
| { final Label label= new Label(composite, SWT.NONE); |
| label.setText("Include files ('R_INCLUDE_DIR'):"); |
| label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); |
| |
| final ResourceInputComposite text= new ResourceInputComposite(composite, ResourceInputComposite.STYLE_TEXT, |
| (ResourceInputComposite.MODE_DIRECTORY | ResourceInputComposite.MODE_OPEN), "R_INCLUDE_DIR"); //$NON-NLS-1$ |
| text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); |
| text.setShowInsertVariable(true, VariableFilterUtils.DEFAULT_NON_ITERACTIVE_FILTERS, null); |
| this.rIncludeDirectoryControl= text; |
| } |
| return composite; |
| } |
| |
| @Override |
| protected void addBindings(final DataBindingSupport db) { |
| // don't specify IREnvConfiguration.WorkingCopy for BeanProperties (missing with getter) |
| db.getContext().bindValue( |
| WidgetProperties.text(SWT.Modify) |
| .observe(this.nameControl), |
| BeanProperties.value(IREnvConfiguration.PROP_NAME, String.class) |
| .observe(this.configModel), |
| new UpdateValueStrategy<String, String>() |
| .setAfterGetValidator((final String value) -> { |
| final String s= value.trim(); |
| if (s.isEmpty()) { |
| return ValidationStatus.error(Messages.REnv_Detail_Name_error_Missing_message); |
| } |
| if (LocalREnvConfigDialog.this.existingNames.contains(s)) { |
| return ValidationStatus.error(Messages.REnv_Detail_Name_error_Duplicate_message); |
| } |
| if (s.contains("/")) { //$NON-NLS-1$ |
| return ValidationStatus.error(Messages.REnv_Detail_Name_error_InvalidChar_message); |
| } |
| return ValidationStatus.ok(); |
| }), |
| null ); |
| if (this.rHomeControl != null) { |
| final Binding rHomeBinding= db.getContext().bindValue( |
| this.rHomeControl.getObservable(), |
| BeanProperties.value(IREnvConfiguration.PROP_RHOME, String.class) |
| .observe(this.configModel), |
| new UpdateValueStrategy<String, String>() |
| .setAfterGetValidator((final String value) -> { |
| final IStatus status= LocalREnvConfigDialog.this.rHomeControl.getValidator().validate(value); |
| if (!status.isOK()) { |
| return status; |
| } |
| if (!LocalREnvConfigDialog.this.configModel.isValidRHomeLocation(LocalREnvConfigDialog.this.rHomeControl.getResourceAsFileStore())) { |
| return ValidationStatus.error(Messages.REnv_Detail_Location_error_NoRHome_message); |
| } |
| updateArchs(!LocalREnvConfigDialog.this.isNewConfig); |
| return ValidationStatus.ok(); |
| }), |
| null ); |
| rHomeBinding.getValidationStatus().addValueChangeListener(new IValueChangeListener<IStatus>() { |
| @Override |
| public void handleValueChange(final ValueChangeEvent<? extends IStatus> event) { |
| final IStatus status= event.diff.getNewValue(); |
| LocalREnvConfigDialog.this.loadButton.setEnabled(status.isOK()); |
| } |
| }); |
| rHomeBinding.validateTargetToModel(); |
| } |
| db.getContext().bindValue( |
| WidgetProperties.text() |
| .observe(this.rArchControl), |
| BeanProperties.value(IREnvConfiguration.PROP_SUBARCH, String.class) |
| .observe(this.configModel) ); |
| |
| if (this.rDocDirectoryControl != null) { |
| db.getContext().bindValue( |
| this.rDocDirectoryControl.getObservable(), |
| BeanProperties.value(IREnvConfiguration.PROP_RDOC_DIRECTORY, String.class) |
| .observe(this.configModel) ); |
| db.getContext().bindValue( |
| this.rShareDirectoryControl.getObservable(), |
| BeanProperties.value(IREnvConfiguration.PROP_RSHARE_DIRECTORY, String.class) |
| .observe(this.configModel) ); |
| db.getContext().bindValue( |
| this.rIncludeDirectoryControl.getObservable(), |
| BeanProperties.value(IREnvConfiguration.PROP_RINCLUDE_DIRECTORY, String.class) |
| .observe(this.configModel) ); |
| } |
| } |
| |
| private String[] searchRHOME() { |
| try { |
| final IStringVariableManager variables= VariablesPlugin.getDefault().getStringVariableManager(); |
| |
| { String loc= variables.performStringSubstitution("${env_var:R_HOME}", false); //$NON-NLS-1$ |
| if (loc != null && loc.length() > 0) { |
| loc= resolve(loc); |
| if (loc != null) { |
| final IFileStore locStore= EFS.getLocalFileSystem().getStore(new Path(loc)); |
| if (locStore.fetchInfo().exists()) { |
| return new String[] { loc, Messages.REnv_SystemRHome_name }; |
| } |
| } |
| } |
| } |
| |
| final ImList<String> locCandidates; |
| String prefixPattern= null; |
| String prefixReplacement= null; |
| if (Platform.getOS().startsWith("win")) { //$NON-NLS-1$ |
| String baseLoc= "${env_var:PROGRAMFILES}\\R"; //$NON-NLS-1$ |
| final IFileStore baseStore= EFS.getLocalFileSystem().getStore( |
| new Path(variables.performStringSubstitution(baseLoc))); |
| if (baseStore.fetchInfo().exists()) { |
| prefixReplacement= baseLoc; |
| prefixPattern= baseLoc= FileUtil.toString(baseStore); |
| |
| final String[] names= baseStore.childNames(EFS.NONE, null); |
| Arrays.sort(names, 0, names.length, |
| Collections.reverseOrder(Collator.getInstance()) ); |
| for (int i= 0; i < names.length; i++) { |
| names[i]= baseLoc + '\\' + names[i]; |
| } |
| locCandidates= ImCollections.newList(names); |
| } |
| else { |
| locCandidates= ImCollections.newList(); |
| } |
| } |
| else if (Platform.getOS().equals(Platform.OS_MACOSX)) { |
| locCandidates= ImCollections.newList( |
| "/Library/Frameworks/R.framework/Resources" //$NON-NLS-1$ |
| ); |
| } |
| else { |
| locCandidates= ImCollections.newList( |
| "/usr/local/lib64/R", //$NON-NLS-1$ |
| "/usr/lib64/R", //$NON-NLS-1$ |
| "/usr/local/lib/R", //$NON-NLS-1$ |
| "/usr/lib/R" //$NON-NLS-1$ |
| ); |
| } |
| for (String loc : locCandidates) { |
| loc= resolve(loc); |
| if (loc != null) { |
| final IFileStore locStore= EFS.getLocalFileSystem().getStore(new Path(loc)); |
| if (this.configModel.isValidRHomeLocation(locStore)) { |
| if (prefixPattern != null && loc.startsWith(prefixPattern)) { |
| loc= prefixReplacement + loc.substring(prefixPattern.length()); |
| } |
| String name= locStore.getName(); |
| if (name.equals("Resources")) { //$NON-NLS-1$ |
| final IFileStore parent= locStore.getParent(); |
| name= (parent != null) ? parent.getName() : null; |
| } |
| if (name != null) { |
| if (name.isEmpty() || name.equals("R")) { //$NON-NLS-1$ |
| name= null; |
| } |
| else if (Character.isDigit(name.charAt(0))) { |
| name= "R " + name; //$NON-NLS-1$ |
| } |
| } |
| return new String[] { loc, name }; |
| } |
| } |
| } |
| return null; |
| } |
| catch (final Exception e) { |
| RUIPlugin.logError(-1, "Error when searching R_HOME location", e); //$NON-NLS-1$ |
| return null; |
| } |
| } |
| |
| private String resolve(final String loc) { |
| try { |
| java.nio.file.Path path= Paths.get(loc); |
| path= path.toRealPath(); |
| return path.toString(); |
| } |
| catch (final IOException e2) { |
| return null; |
| } |
| } |
| |
| private void updateArchs(final boolean conservative) { |
| if (this.rHomeControl == null) { |
| return; |
| } |
| try { |
| final IFileStore rHome= this.rHomeControl.getResourceAsFileStore(); |
| final List<String> availableArchs= this.configModel.searchAvailableSubArchs(rHome); |
| if (availableArchs == null) { |
| this.rArchControl.setItems(new String[0]); |
| return; |
| } |
| final String oldArch= this.rArchControl.getText(); |
| this.rArchControl.setItems(availableArchs.toArray(new String[availableArchs.size()])); |
| int idx= (oldArch.length() > 0) ? availableArchs.indexOf(oldArch) : -1; |
| if (idx >= 0) { |
| this.rArchControl.select(idx); |
| } |
| |
| if (conservative && this.rArchControl.getText().length() > 0) { |
| return; |
| } |
| idx= availableArchs.indexOf(Platform.getOSArch()); |
| if (idx < 0) { |
| if (Platform.getOSArch().equals(Platform.ARCH_X86)) { |
| idx= availableArchs.indexOf("i386"); //$NON-NLS-1$ |
| if (idx < 0) { |
| idx= availableArchs.indexOf("i586"); //$NON-NLS-1$ |
| if (idx < 0) { |
| idx= availableArchs.indexOf("i686"); //$NON-NLS-1$ |
| } |
| } |
| } |
| } |
| if (idx < 0) { |
| idx= 0; |
| } |
| this.rArchControl.select(idx); |
| } |
| catch (final Exception e) { |
| this.rArchControl.setItems(new String[0]); |
| } |
| } |
| |
| private void detectSettings() { |
| try { |
| run(true, true, new IRunnableWithProgress() { |
| @Override |
| public void run(final IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { |
| try { |
| detectSettings(monitor); |
| } |
| catch (final CoreException e) { |
| throw new InvocationTargetException(e); |
| } |
| } |
| }); |
| } |
| catch (final InvocationTargetException e) { |
| final String message= (e.getCause() instanceof CoreException) ? |
| Messages.REnv_Detail_DetectSettings_error_message : |
| Messages.REnv_Detail_DetectSettings_error_Unexpected_message; |
| StatusManager.getManager().handle(new Status(IStatus.ERROR, RUI.BUNDLE_ID, -1, |
| message, e), StatusManager.LOG); |
| StatusManager.getManager().handle(new Status(IStatus.ERROR, RUI.BUNDLE_ID, -1, |
| message, e.getCause()), StatusManager.SHOW); |
| } |
| catch (final InterruptedException e) { |
| } |
| this.rLibrariesButtons.refresh(); |
| this.rLibrariesViewer.expandAll(); |
| } |
| |
| private void detectSettings(final IProgressMonitor monitor) throws CoreException { |
| monitor.beginTask(Messages.REnv_Detail_DetectSettings_task, 10); |
| |
| final ProcessBuilder processBuilder= new ProcessBuilder(this.configModel.getExecCommand(Exec.TERM)); |
| processBuilder.command().add("--no-save"); //$NON-NLS-1$ |
| processBuilder.command().add("--slave"); //$NON-NLS-1$ |
| processBuilder.command().add("-e"); //$NON-NLS-1$ |
| processBuilder.command().add(DETECT_COMMAND); |
| |
| final Map<String, String> envp= processBuilder.environment(); |
| try { |
| final DefaultLocalConfigurator setup= new DefaultLocalConfigurator(this.configModel); |
| LaunchUtils.configureEnvironment(envp, null, |
| setup.getEnvironmentsVariables(DefaultLocalConfigurator.MINIMAL_SETUP) ); |
| } |
| catch (final StatusException e) { |
| throw StatusUtils.convert(e); |
| } |
| |
| monitor.worked(1); |
| |
| final ProcessOutputCollector reader= new ProcessOutputCollector(processBuilder, "'Detect R settings'", monitor); //$NON-NLS-1$ |
| final String output= reader.collect(); |
| final int start= output.indexOf(DETECT_START); |
| if (start >= 0) { |
| final String[] lines= DETECT_ITEM_PATTERN.split(output.substring(start)); |
| if (lines.length == DETECT_LENGTH) { |
| updateLibraries(this.configModel.getRLibGroup(RLibGroup.R_DEFAULT), |
| lines[DETECT_R_DEFAULT], lines[DETECT_R_HOME]); |
| |
| final RLibGroupWorkingCopy group= this.configModel.getRLibGroup(RLibGroup.R_SITE); |
| updateLibraries(group, lines[DETECT_R_SITE], lines[DETECT_R_HOME]); |
| if (group.getLibLocations().isEmpty()) { |
| group.getLibLocations().add(group.newLibrary(BasicREnvConfiguration.DEFAULT_R_SITE_LOCATION_DIRECTORY)); |
| } |
| updateLibraries(this.configModel.getRLibGroup(RLibGroup.R_OTHER), |
| lines[DETECT_R_OTHER], lines[DETECT_R_HOME]); |
| updateLibraries(this.configModel.getRLibGroup(RLibGroup.R_USER), |
| lines[DETECT_R_USER], lines[DETECT_R_HOME]); |
| |
| this.configModel.setRDocDirectory(checkDir(lines[DETECT_R_DOC_DIR], lines[DETECT_R_HOME])); |
| this.configModel.setRShareDirectory(checkDir(lines[DETECT_R_SHARE_DIR], lines[DETECT_R_HOME])); |
| this.configModel.setRIncludeDirectory(checkDir(lines[DETECT_R_INCLUDE_DIR], lines[DETECT_R_HOME])); |
| |
| if (lines[DETECT_R_ARCHVAR].length() > 0) { |
| this.configModel.setRArch(lines[DETECT_R_ARCHVAR]); |
| } |
| else if (lines[DETECT_R_ARCH].length() > 0 && this.configModel.getRArch() == null) { |
| this.configModel.setRArch(lines[DETECT_R_ARCH]); |
| } |
| this.configModel.setROS(lines[DETECT_R_OS]); |
| return; |
| } |
| } |
| throw new CoreException(new Status(IStatus.ERROR, RUI.BUNDLE_ID, -1, |
| "Unexpected output:\n" + output, null)); //$NON-NLS-1$ |
| } |
| |
| private void updateLibraries(final RLibGroupWorkingCopy group, final String var, final String rHome) { |
| final List<RLibLocation> libraries= group.getLibLocations(); |
| libraries.clear(); |
| final String[] locations= DETECT_PATH_PATTERN.split(var); |
| final IPath rHomePath= new Path(rHome); |
| final IPath userHomePath= new Path(System.getProperty("user.home")); //$NON-NLS-1$ |
| for (final String location : locations) { |
| if (location.isEmpty()) { |
| continue; |
| } |
| String s; |
| final IPath path; |
| if (location.startsWith("~/")) { //$NON-NLS-1$ |
| path= userHomePath.append(location.substring(2)); |
| } |
| else { |
| path= new Path(location); |
| } |
| if (rHomePath.isPrefixOf(path)) { |
| s= R_HOME_DIRECTORY_VAR_STRING + '/' + path.makeRelativeTo(rHomePath).toString(); |
| } |
| else if (userHomePath.isPrefixOf(path)) { |
| s= "${user_home}/" + path.makeRelativeTo(userHomePath).toString(); //$NON-NLS-1$ |
| } |
| else { |
| s= path.toString(); |
| } |
| libraries.add(group.newLibrary(s)); |
| } |
| } |
| |
| private String checkDir(String dir, final String rHome) { |
| if (dir != null && dir.length() > 0) { |
| final IPath rHomePath= new Path(rHome); |
| final IPath path= new Path(dir); |
| if (rHomePath.isPrefixOf(path)) { |
| dir= R_HOME_DIRECTORY_VAR_STRING + '/' + path.makeRelativeTo(rHomePath).toString(); |
| } |
| return dir; |
| } |
| return null; |
| } |
| |
| } |