| /*=============================================================================# |
| # Copyright (c) 2009, 2020 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.ui; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.eclipse.core.databinding.UpdateValueStrategy; |
| import org.eclipse.core.databinding.observable.Realm; |
| import org.eclipse.core.databinding.observable.list.WritableList; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IProjectDescription; |
| import org.eclipse.core.resources.IResourceChangeEvent; |
| import org.eclipse.core.resources.IResourceChangeListener; |
| 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.Status; |
| import org.eclipse.core.runtime.SubMonitor; |
| import org.eclipse.jface.resource.JFaceResources; |
| 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.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.TabFolder; |
| import org.eclipse.swt.widgets.TabItem; |
| import org.eclipse.swt.widgets.Text; |
| import org.eclipse.ui.ISharedImages; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.actions.WorkspaceModifyOperation; |
| import org.eclipse.ui.statushandlers.StatusManager; |
| |
| import org.eclipse.statet.jcommons.collections.ImList; |
| |
| import org.eclipse.statet.ecommons.databinding.jface.DataBindingSupport; |
| import org.eclipse.statet.ecommons.preferences.core.Preference; |
| import org.eclipse.statet.ecommons.preferences.ui.ConfigurationBlock; |
| import org.eclipse.statet.ecommons.preferences.ui.ManagedConfigurationBlock; |
| import org.eclipse.statet.ecommons.preferences.ui.PropertyAndPreferencePage; |
| import org.eclipse.statet.ecommons.resources.core.ProjectUtils; |
| import org.eclipse.statet.ecommons.runtime.core.StatusChangeListener; |
| import org.eclipse.statet.ecommons.ui.util.LayoutUtils; |
| import org.eclipse.statet.ecommons.ui.util.UIAccess; |
| |
| import org.eclipse.statet.internal.r.ui.buildpaths.RBuildpathsUIDescription; |
| import org.eclipse.statet.ltk.buildpaths.core.IBuildpathElement; |
| import org.eclipse.statet.ltk.buildpaths.ui.SourceContainerComponent; |
| import org.eclipse.statet.r.core.RBuildpaths; |
| import org.eclipse.statet.r.core.RProject; |
| import org.eclipse.statet.r.core.RProjects; |
| import org.eclipse.statet.r.ui.RUI; |
| |
| |
| public class RProjectPropertyPage extends PropertyAndPreferencePage { |
| |
| |
| public RProjectPropertyPage() { |
| } |
| |
| |
| @Override |
| protected String getPreferencePageID() { |
| return null; |
| } |
| |
| @Override |
| protected String getPropertyPageID() { |
| return "org.eclipse.statet.r.propertyPages.RProject"; //$NON-NLS-1$ |
| } |
| |
| |
| @Override |
| protected ConfigurationBlock createConfigurationBlock() throws CoreException { |
| return new RProjectConfigurationBlock(getProject(), createStatusChangedListener()); |
| } |
| |
| @Override |
| protected boolean hasProjectSpecificSettings(final IProject project) { |
| return true; |
| } |
| |
| } |
| |
| |
| class RProjectConfigurationBlock extends ManagedConfigurationBlock { |
| |
| |
| private final RProject rProject; |
| |
| private final RBuildpathsUIDescription buildpathUIDescription; |
| private final WritableList buildpathList; |
| |
| private final SourceContainerComponent sourceFolders; |
| |
| private boolean isPkg; |
| private Label pkgNameLabel; |
| private Text pkgNameControl; |
| |
| private RProjectContainerComposite projectComposite; |
| |
| private REnvSelectionComposite rEnvControl; |
| |
| private IResourceChangeListener listener; |
| |
| |
| public RProjectConfigurationBlock(final IProject project, final StatusChangeListener statusListener) { |
| super(project, statusListener); |
| |
| this.rProject= RProjects.getRProject(project); |
| |
| this.buildpathUIDescription= new RBuildpathsUIDescription(); |
| this.buildpathList= new WritableList<>(); |
| this.sourceFolders= new SourceContainerComponent(this.buildpathList, |
| RBuildpaths.R_SOURCE_TYPE, null, this.buildpathUIDescription ); |
| |
| this.listener= new IResourceChangeListener() { |
| @Override |
| public void resourceChanged(final IResourceChangeEvent event) { |
| UIAccess.getDisplay().asyncExec(() -> { |
| if (UIAccess.isOkToUse(RProjectConfigurationBlock.this.pkgNameControl)) { |
| updatePkgName(); |
| } |
| }); |
| } |
| }; |
| this.rProject.getProject().getWorkspace().addResourceChangeListener(this.listener, |
| IResourceChangeEvent.POST_BUILD ); |
| } |
| |
| @Override |
| public void dispose() { |
| if (this.listener != null) { |
| this.rProject.getProject().getWorkspace().removeResourceChangeListener(this.listener); |
| this.listener= null; |
| } |
| |
| super.dispose(); |
| } |
| |
| |
| @Override |
| protected String getHelpContext() { |
| return RUI.BUNDLE_ID + ".r_project-project_properties"; //$NON-NLS-1$ |
| } |
| |
| @Override |
| protected void createBlockArea(final Composite pageComposite) { |
| final Map<Preference<?>, String> prefs= new HashMap<>(); |
| |
| prefs.put(RProject.PKG_BASE_FOLDER_PATH_PREF, null); |
| prefs.put(RProject.RENV_CODE_PREF, null); |
| |
| setupPreferenceManager(prefs); |
| |
| final Composite mainComposite= new Composite(pageComposite, SWT.NONE); |
| mainComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| mainComposite.setLayout(LayoutUtils.newCompositeGrid(2)); |
| |
| final TabFolder folder = new TabFolder(mainComposite, SWT.NONE); |
| folder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); |
| |
| { final TabItem item = new TabItem(folder, SWT.NONE); |
| item.setText("&Main"); |
| item.setControl(createMainTab(folder)); |
| } |
| { final TabItem item= new TabItem(folder, SWT.NONE); |
| item.setImage(PlatformUI.getWorkbench().getSharedImages().getImage( |
| ISharedImages.IMG_OBJ_FOLDER )); |
| item.setText("&Source"); |
| item.setControl(createSourceTab(folder)); |
| } |
| { final TabItem item= new TabItem(folder, SWT.NONE); |
| item.setImage(RUI.getImage(RUI.IMG_OBJ_R_RUNTIME_ENV)); |
| item.setText("R &Environment"); |
| item.setControl(createREnvTab(folder)); |
| } |
| |
| initBindings(); |
| updateControls(); |
| } |
| |
| private Control createMainTab(final Composite parent) { |
| final Composite composite= new Composite(parent, SWT.NONE); |
| composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| composite.setLayout(LayoutUtils.newTabGrid(2)); |
| |
| this.projectComposite= new RProjectContainerComposite(composite, getProject()); |
| this.projectComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| |
| final Composite buttons= new Composite(composite, SWT.NONE); |
| buttons.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, true)); |
| buttons.setLayout(LayoutUtils.newCompositeGrid(1)); |
| |
| { final Button button= new Button(buttons, SWT.PUSH); |
| button.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); |
| button.setText("R &Package Root"); |
| button.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(final SelectionEvent e) { |
| RProjectConfigurationBlock.this.projectComposite.togglePkgBaseContainer(); |
| } |
| }); |
| } |
| |
| { final Composite packageComposite= new Composite(composite, SWT.NONE); |
| packageComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); |
| packageComposite.setLayout(LayoutUtils.newCompositeGrid(2)); |
| |
| final Label label= new Label(packageComposite, SWT.NONE); |
| label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); |
| label.setText("R Package &Name:"); |
| this.pkgNameLabel= label; |
| |
| this.pkgNameControl= new Text(packageComposite, SWT.READ_ONLY); |
| this.pkgNameControl.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); |
| this.pkgNameControl.setFont(JFaceResources.getTextFont()); |
| this.pkgNameControl.setEditable(false); |
| } |
| |
| return composite; |
| } |
| |
| private Control createSourceTab(final Composite parent) { |
| final Composite composite= new Composite(parent, SWT.NONE); |
| composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| composite.setLayout(LayoutUtils.newTabGrid(1)); |
| |
| { final Control control= this.sourceFolders.create(composite); |
| control.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| } |
| |
| return composite; |
| } |
| |
| private Control createREnvTab(final Composite parent) { |
| final Composite composite= new Composite(parent, SWT.NONE); |
| composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); |
| composite.setLayout(LayoutUtils.newTabGrid(1)); |
| |
| { final Label label= new Label(composite, SWT.NONE); |
| label.setText("R Envir&onment:"); |
| } |
| this.rEnvControl= new REnvSelectionComposite(composite); |
| this.rEnvControl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); |
| |
| return composite; |
| } |
| |
| |
| @Override |
| protected void addBindings(final DataBindingSupport db) { |
| db.getContext().bindValue( |
| this.rEnvControl.createObservable(Realm.getDefault()), |
| createObservable(RProject.RENV_CODE_PREF), |
| new UpdateValueStrategy<String, String>() |
| .setAfterGetValidator(this.rEnvControl.createValidator(db.getContext())), |
| null ); |
| |
| this.sourceFolders.bind(db); |
| } |
| |
| private void updatePkgName() { |
| final IPath pkgBasePath= this.rProject.getPkgRootPath(); |
| if (pkgBasePath != null) { |
| String pkgName= this.rProject.getPkgName(); |
| if (pkgName == null) { |
| pkgName= "<missing>"; |
| } |
| this.pkgNameLabel.setEnabled(true); |
| this.pkgNameControl.setText(pkgName); |
| } |
| else { |
| this.pkgNameLabel.setEnabled(false); |
| this.pkgNameControl.setText(""); //$NON-NLS-1$ |
| } |
| } |
| |
| @Override |
| protected void updateControls() { |
| final IPath pkgBasePath= this.rProject.getPkgRootPath(); |
| if (pkgBasePath != null) { |
| this.isPkg= true; |
| this.projectComposite.setPkgBaseContainer(pkgBasePath.removeFirstSegments(1)); |
| } |
| else { |
| this.isPkg= false; |
| this.projectComposite.setPkgBaseContainer(null); |
| } |
| |
| updatePkgName(); |
| |
| { this.buildpathList.clear(); |
| this.buildpathUIDescription.toListElements(getProject(), this.rProject.getRawBuildpath(), |
| this.buildpathList ); |
| this.sourceFolders.init(getProject()); |
| } |
| |
| super.updateControls(); |
| } |
| |
| @Override |
| protected void updatePreferences() { |
| final IPath pkgBasePath= this.projectComposite.getPkgBaseContainer(); |
| if (pkgBasePath != null) { |
| this.isPkg= true; |
| setPrefValue(RProject.PKG_BASE_FOLDER_PATH_PREF, pkgBasePath.toPortableString()); |
| } |
| else { |
| this.isPkg= false; |
| setPrefValue(RProject.PKG_BASE_FOLDER_PATH_PREF, null); |
| } |
| |
| super.updatePreferences(); |
| } |
| |
| @Override |
| public boolean performOk(final int flags) { |
| final boolean wasPkg= this.isPkg; |
| |
| saveProjectConfig(); |
| final boolean ok= super.performOk(flags); |
| |
| if (ok && wasPkg != this.isPkg) { |
| setupRPkg(this.isPkg); |
| } |
| |
| return ok; |
| } |
| |
| private void saveProjectConfig() { |
| final ImList<IBuildpathElement> newBuildpath= this.buildpathUIDescription.toCoreElements(this.buildpathList); |
| RBuildpaths.set(getProject(), newBuildpath); |
| } |
| |
| private void setupRPkg(final boolean pkg) { |
| final WorkspaceModifyOperation op= new WorkspaceModifyOperation() { |
| |
| @Override |
| protected void execute(final IProgressMonitor monitor) throws CoreException, |
| InvocationTargetException, InterruptedException { |
| final SubMonitor m= SubMonitor.convert(monitor); |
| try { |
| final IProject project= RProjectConfigurationBlock.this.rProject.getProject(); |
| if (pkg) { |
| RProjects.setupRPkgProject(project, null, m); |
| } |
| else { |
| m.beginTask( |
| NLS.bind(RUIMessages.RProject_ConfigureTask_label, project.getName()), |
| 2 + 8 ); |
| |
| final IProjectDescription description= project.getDescription(); |
| boolean changed= false; |
| changed|= ProjectUtils.removeNature(description, RProjects.R_PKG_NATURE_ID); |
| m.worked(2); |
| |
| if (changed) { |
| project.setDescription(description, m.newChild(8)); |
| } |
| } |
| } |
| finally { |
| m.done(); |
| } |
| } |
| |
| }; |
| try { |
| UIAccess.getActiveWorkbenchWindow(true).run(true, true, op); |
| } |
| catch (final InvocationTargetException e) { |
| StatusManager.getManager().handle(new Status(IStatus.ERROR, RUI.BUNDLE_ID, |
| RUIMessages.RPkgProject_ConvertTask_error_message, e.getTargetException() )); |
| } |
| catch (final InterruptedException e) { |
| // cancelled |
| } |
| } |
| |
| @Override |
| protected String[] getFullBuildDialogStrings(final boolean workspaceSettings) { |
| return new String[] { |
| RUIMessages.RProject_NeedsBuild_title, |
| (workspaceSettings) ? |
| RUIMessages.RProject_NeedsBuild_Full_message : |
| RUIMessages.RProject_NeedsBuild_Project_message |
| }; |
| } |
| |
| } |