blob: 68f597da6bb27f386f56e293083fe8f3992e64f3 [file] [log] [blame]
/*=============================================================================#
# 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
};
}
}