blob: 562fc9f937c1cc3404fc981f30d39e67e056c7d6 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2007, 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.console.ui.launching;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
import org.eclipse.core.databinding.observable.value.WritableValue;
import org.eclipse.core.databinding.validation.IValidator;
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.ui.ILaunchConfigurationDialog;
import org.eclipse.debug.ui.ILaunchConfigurationTab;
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.dialogs.TrayDialog;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.LabelProvider;
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.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
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.ui.PlatformUI;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.statet.ecommons.databinding.core.validation.UpdateableErrorValidator;
import org.eclipse.statet.ecommons.debug.core.util.LaunchUtils;
import org.eclipse.statet.ecommons.debug.ui.config.InputArgumentsComposite;
import org.eclipse.statet.ecommons.debug.ui.config.LaunchConfigTabWithDbc;
import org.eclipse.statet.ecommons.debug.ui.util.HelpRequestor;
import org.eclipse.statet.ecommons.ui.SharedMessages;
import org.eclipse.statet.ecommons.ui.SharedUIResources;
import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
import org.eclipse.statet.ecommons.ui.util.VariableFilterUtils;
import org.eclipse.statet.ecommons.ui.workbench.ResourceInputComposite;
import org.eclipse.statet.internal.r.console.ui.Messages;
import org.eclipse.statet.internal.r.console.ui.RConsoleUIPlugin;
import org.eclipse.statet.r.console.ui.IRConsoleHelpContextIds;
import org.eclipse.statet.r.console.ui.launching.RConsoleLaunching;
import org.eclipse.statet.r.core.renv.IREnvConfiguration;
import org.eclipse.statet.r.core.renv.IREnvConfiguration.Exec;
import org.eclipse.statet.r.launching.core.RLaunching;
import org.eclipse.statet.r.launching.ui.REnvTab;
/**
* Main tab for R Console launch config.
*/
public class RConsoleMainTab extends LaunchConfigTabWithDbc {
private class RArgumentsComposite extends InputArgumentsComposite {
public RArgumentsComposite(final Composite parent) {
super(parent);
}
@Override
protected void fillToolMenu(final Menu menu) {
super.fillToolMenu(menu);
if (RConsoleMainTab.this.withHelp) {
RConsoleMainTab.this.helpItem= new MenuItem(menu, SWT.PUSH);
RConsoleMainTab.this.helpItem.setText(Messages.RConsole_MainTab_RunHelp_label);
RConsoleMainTab.this.helpItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
queryHelp();
getTextControl().setFocus();
}
});
checkHelp(RConsoleMainTab.this.configCache);
}
}
}
private final RConsoleType[] types;
private final RConsoleType defaultType;
private final IObservableValue<RConsoleType> typeValue;
private final IObservableValue<String> workingDirectoryValue;
protected final IObservableValue<String> argumentsValue;
private ComboViewer typesCombo;
private ResourceInputComposite workingDirectoryControl;
private RArgumentsComposite argumentsControl;
boolean withHelp= false;
private MenuItem helpItem;
private ILaunchConfigurationTab rEnvTab;
private ILaunchConfiguration configCache;
public RConsoleMainTab() {
super();
this.types= loadTypes();
this.defaultType= this.types[0];
final Realm realm= getRealm();
this.typeValue= new WritableValue<>(realm, null, RConsoleType.class);
this.workingDirectoryValue= new WritableValue<>(realm, null, String.class);
this.argumentsValue= new WritableValue<>(realm, "", String.class); //$NON-NLS-1$
}
protected RConsoleType[] loadTypes() {
final List<RConsoleType> types= new ArrayList<>();
types.add(new RConsoleType("RJ (default)", RConsoleLaunching.LOCAL_RJS, true, true, true)); //$NON-NLS-1$
types.add(new RConsoleType("Rterm", RConsoleLaunching.LOCAL_RTERM, false, false, false)); //$NON-NLS-1$
return types.toArray(new RConsoleType[types.size()]);
}
RConsoleType getSelectedType() {
if (this.typeValue != null) {
return this.typeValue.getValue();
}
return null;
}
@Override
public String getName() {
return Messages.RConsole_MainTab_name;
}
@Override
public Image getImage() {
return SharedUIResources.getImages().get(SharedUIResources.OBJ_MAIN_TAB_ID);
}
@Override
public void createControl(final Composite parent) {
final Composite mainComposite= new Composite(parent, SWT.NONE);
setControl(mainComposite);
mainComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
mainComposite.setLayout(new GridLayout());
{ // Type
final Composite composite= new Composite(mainComposite, SWT.NONE);
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
composite.setLayout(LayoutUtils.newCompositeGrid(2));
final Label label= new Label(composite, SWT.LEFT);
label.setText(Messages.RConsole_MainTab_LaunchType_label+':');
label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
final String[] names= new String[this.types.length];
for (int i= 0; i < this.types.length; i++) {
names[i]= this.types[i].getName();
}
this.typesCombo= new ComboViewer(composite, SWT.DROP_DOWN | SWT.READ_ONLY);
this.typesCombo.setContentProvider(new ArrayContentProvider());
this.typesCombo.setLabelProvider(new LabelProvider() {
@Override
public String getText(final Object element) {
final RConsoleType type= (RConsoleType) element;
return type.getName();
}
});
this.typesCombo.setInput(this.types);
this.typesCombo.getCombo().setVisibleItemCount(names.length);
this.typesCombo.getControl().setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
this.typesCombo.getControl().setEnabled(this.types.length > 1);
}
final Composite detailGroup= createTypeDetailGroup(mainComposite);
if (detailGroup != null) {
detailGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
}
final Composite commandGroup= createROptionsGroup(mainComposite);
commandGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
LayoutUtils.addSmallFiller(mainComposite, true);
createFooter(mainComposite);
Dialog.applyDialogFont(parent);
initBindings();
PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(),
IRConsoleHelpContextIds.R_CONSOLE_LAUNCH );
}
private Composite createROptionsGroup(final Composite parent) {
for (final ILaunchConfigurationTab tab : getLaunchConfigurationDialog().getTabs()) {
if (tab instanceof REnvTab) {
this.rEnvTab= tab;
break;
}
}
this.withHelp= (this.rEnvTab != null) && (getLaunchConfigurationDialog() instanceof TrayDialog);
final Group group= new Group(parent, SWT.NONE);
group.setLayout(LayoutUtils.newGroupGrid(3));
group.setText("R options:");
this.workingDirectoryControl= new ResourceInputComposite(group,
ResourceInputComposite.STYLE_LABEL | ResourceInputComposite.STYLE_TEXT,
ResourceInputComposite.MODE_DIRECTORY | ResourceInputComposite.MODE_OPEN,
Messages.RConsole_MainTab_WorkingDir_label );
this.workingDirectoryControl.setShowInsertVariable(true,
VariableFilterUtils.DEFAULT_INTERACTIVE_FILTERS, null);
this.workingDirectoryControl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));
this.argumentsControl= new RArgumentsComposite(group);
this.argumentsControl.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));
return group;
}
protected Composite createTypeDetailGroup(final Composite parent) {
return null;
}
protected Composite getArgumentComposite() {
return this.argumentsControl;
}
protected void createFooter(final Composite composite) {
final Label note= new Label(composite, SWT.WRAP);
note.setText(SharedMessages.Note_label + ": " + this.argumentsControl.getNoteText()); //$NON-NLS-1$
note.setLayoutData(new GridData(SWT.FILL, SWT.BOTTOM, true, false));
}
@Override
protected void addBindings(final DataBindingContext dbc, final Realm realm) {
IValidator<RConsoleType> typeValidator= null;
if (getLaunchConfigurationDialog().getMode().equals(ILaunchManager.DEBUG_MODE)) {
typeValidator= new UpdateableErrorValidator<>((final RConsoleType type) -> {
if (!type.isDebugSupported()) {
return ValidationStatus.error(NLS.bind(
"R launch type ''{0}'' does not support debug mode.",
type.getName() ));
}
return ValidationStatus.ok();
});
}
dbc.bindValue(
ViewerProperties.singleSelection(RConsoleType.class)
.observe(this.typesCombo),
this.typeValue,
(typeValidator != null) ?
new UpdateValueStrategy<RConsoleType, RConsoleType>()
.setAfterGetValidator(typeValidator) :
null,
null );
dbc.bindValue(
WidgetProperties.text(SWT.Modify)
.observe(this.argumentsControl.getTextControl()),
this.argumentsValue );
this.workingDirectoryControl.getValidator().setOnEmpty(IStatus.OK);
dbc.bindValue(
this.workingDirectoryControl.getObservable(),
this.workingDirectoryValue,
new UpdateValueStrategy<String, String>()
.setAfterGetValidator(new UpdateableErrorValidator<>(
this.workingDirectoryControl.getValidator() )),
null );
this.typeValue.addValueChangeListener(new IValueChangeListener<RConsoleType>() {
@Override
public void handleValueChange(final ValueChangeEvent<? extends RConsoleType> event) {
final Object newValue= event.diff.getNewValue();
updateType((RConsoleType) newValue);
}
});
}
public RConsoleType getType() {
return this.typeValue.getValue();
}
protected IObservableValue<RConsoleType> getTypeValue() {
return this.typeValue;
}
/**
* @param typeId
*/
protected void updateType(final RConsoleType type) {
}
@Override
public void setDefaults(final ILaunchConfigurationWorkingCopy configuration) {
configuration.setAttribute(RConsoleLaunching.ATTR_TYPE, this.defaultType.getId()); //s
configuration.setAttribute(RConsoleLaunching.ATTR_OPTIONS, ""); //$NON-NLS-1$
configuration.setAttribute(RConsoleLaunching.ATTR_PIN_CONSOLE, false);
}
@Override
protected void doInitialize(final ILaunchConfiguration configuration) {
{ String type= readAttribute(configuration,
RConsoleLaunching.ATTR_TYPE,
"" ); //$NON-NLS-1$
// convert old rterm to new rj
if (type.equals("rterm")) { //$NON-NLS-1$
type= RConsoleLaunching.LOCAL_RTERM;
}
int i= 0;
for (; i < this.types.length; i++) {
if (this.types[i].getId().equals(type)) {
this.typeValue.setValue(this.types[i]);
break;
}
}
if (i >= this.types.length) {
this.typeValue.setValue(this.defaultType);
}
}
{ String wd= null;
try {
wd= REnvTab.readWorkingDirectory(configuration);
}
catch (final CoreException e) {
wd= ""; //$NON-NLS-1$
logReadingError(e);
}
this.workingDirectoryValue.setValue(wd);
}
{ final String options= readAttribute(configuration,
RConsoleLaunching.ATTR_OPTIONS,
"" ); //$NON-NLS-1$
this.argumentsValue.setValue(options);
}
checkHelp(configuration);
}
@Override
public void activated(final ILaunchConfigurationWorkingCopy workingCopy) {
checkHelp(workingCopy);
super.activated(workingCopy);
}
@Override
protected void doSave(final ILaunchConfigurationWorkingCopy configuration) {
configuration.setAttribute(RConsoleLaunching.ATTR_TYPE, this.typeValue.getValue().getId());
if (this.argumentsControl.isEnabled()) {
configuration.setAttribute(RConsoleLaunching.ATTR_OPTIONS, this.argumentsValue.getValue());
}
else {
configuration.removeAttribute(RConsoleLaunching.ATTR_OPTIONS);
}
REnvTab.setWorkingDirectory(configuration, this.workingDirectoryValue.getValue());
}
private void checkHelp(final ILaunchConfiguration configuration) {
this.configCache= configuration;
if (this.withHelp && this.helpItem != null) {
this.helpItem.setEnabled(this.rEnvTab.isValid(this.configCache));
}
}
private void queryHelp() {
if (!this.withHelp) {
return;
}
try {
final List<String> cmdLine= new ArrayList<>();
final ILaunchConfigurationDialog dialog= getLaunchConfigurationDialog();
// r env
final IREnvConfiguration renv= RLaunching.getREnvConfig(this.configCache, true);
cmdLine.addAll(0, renv.getExecCommand(Exec.TERM));
cmdLine.add("--help"); //$NON-NLS-1$
final ProcessBuilder processBuilder= new ProcessBuilder(cmdLine);
final HelpRequestor helper= new HelpRequestor(processBuilder, (TrayDialog) dialog);
final Map<String, String> envp= processBuilder.environment();
LaunchUtils.configureEnvironment(envp, this.configCache, renv.getEnvironmentsVariables());
dialog.run(true, true, helper);
updateLaunchConfigurationDialog();
}
catch (final CoreException e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR, RConsoleUIPlugin.BUNDLE_ID, -1,
Messages.RConsole_MainTab_error_CannotRunHelp_message, e ),
StatusManager.LOG | StatusManager.SHOW);
}
catch (final InvocationTargetException e) {
StatusManager.getManager().handle(new Status(IStatus.ERROR, RConsoleUIPlugin.BUNDLE_ID, -1,
Messages.RConsole_MainTab_error_WhileRunningHelp_message, e.getTargetException() ),
StatusManager.LOG | StatusManager.SHOW);
}
catch (final InterruptedException e) {
// canceled
}
}
@Override
public void dispose() {
if (this.withHelp) {
HelpRequestor.closeHelpTray((TrayDialog) getLaunchConfigurationDialog());
}
super.dispose();
}
}