/**
* Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
/*
* Created on Jan 25, 2005
*
* @author Fabio Zadrozny
*/
package org.python.pydev.ui;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.viewers.LabelProvider;
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.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.dialogs.PropertyPage;
import org.python.pydev.core.IGrammarVersionProvider;
import org.python.pydev.core.IGrammarVersionProvider.AdditionalGrammarVersionsToCheck;
import org.python.pydev.core.IInterpreterInfo;
import org.python.pydev.core.IInterpreterManager;
import org.python.pydev.core.IPythonNature;
import org.python.pydev.core.MisconfigurationException;
import org.python.pydev.core.log.Log;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.plugin.nature.PythonNature;
import org.python.pydev.shared_core.string.FastStringBuffer;
import org.python.pydev.shared_core.string.StringUtils;
import org.python.pydev.shared_core.utils.ArrayUtils;
import org.python.pydev.ui.dialogs.PyDialogHelpers;
import org.python.pydev.ui.dialogs.SelectNDialog;
import org.python.pydev.ui.pythonpathconf.AutoConfigMaker;
import org.python.pydev.ui.pythonpathconf.IInterpreterProviderFactory.InterpreterType;
import org.python.pydev.ui.pythonpathconf.InterpreterConfigHelpers;
import org.python.pydev.utils.ICallback;
/**
* @author Fabio Zadrozny
*/
public class PyProjectPythonDetails extends PropertyPage {
private static final String ADDITIONAL_SYNTAX_PREFIX = "Additional syntax validation: ";
private static final String ADDITIONAL_SYNTAX_NO_SELECTED = ADDITIONAL_SYNTAX_PREFIX
+ "<no additional grammars selected>.";
/**
* This class provides a way to show to the user the options available to configure a project with the
* correct interpreter and grammar.
*/
public static class ProjectInterpreterAndGrammarConfig {
private static final String INTERPRETER_NOT_CONFIGURED_MSG = "<a>Please configure an interpreter before proceeding.</a>";
public Button radioPy;
public Button radioJy;
public Button radioIron;
public Combo comboGrammarVersion;
public Label versionLabel;
public Combo interpretersChoice;
private Link interpreterNoteText;
private SelectionListener selectionListener;
private ICallback onSelectionChanged;
private Label interpreterLabel;
private Label labelAdditionalGrammarsSelected;
public ProjectInterpreterAndGrammarConfig() {
//Don't want to display "config interpreter" dialog when this dialog does that already.
PyDialogHelpers.enableAskInterpreterStep(false);
}
/**
* Optionally, a callback may be passed to be called whenever the selection of the project type changes.
*/
public ProjectInterpreterAndGrammarConfig(ICallback callback) {
this.onSelectionChanged = callback;
}
public Control doCreateContents(Composite p) {
Composite topComp = new Composite(p, SWT.NONE);
GridLayout innerLayout = new GridLayout();
innerLayout.numColumns = 1;
innerLayout.marginHeight = 0;
innerLayout.marginWidth = 0;
topComp.setLayout(innerLayout);
GridData gd = new GridData(GridData.FILL_BOTH);
topComp.setLayoutData(gd);
//Project type
Group group = new Group(topComp, SWT.NONE);
group.setText("Choose the project type");
GridLayout layout = new GridLayout();
layout.horizontalSpacing = 8;
layout.numColumns = 3;
group.setLayout(layout);
gd = new GridData(GridData.FILL_HORIZONTAL);
group.setLayoutData(gd);
radioPy = new Button(group, SWT.RADIO | SWT.LEFT);
radioPy.setText("Python");
radioJy = new Button(group, SWT.RADIO | SWT.LEFT);
radioJy.setText("Jython");
radioIron = new Button(group, SWT.RADIO | SWT.LEFT);
radioIron.setText("IronPython");
//Grammar version
versionLabel = new Label(topComp, 0);
versionLabel.setText("Grammar Version");
gd = new GridData(GridData.FILL_HORIZONTAL);
versionLabel.setLayoutData(gd);
comboGrammarVersion = new Combo(topComp, SWT.READ_ONLY);
for (String s : IPythonNature.Versions.VERSION_NUMBERS) {
s = numberToUi(s);
comboGrammarVersion.add(s);
}
gd = new GridData(GridData.FILL_HORIZONTAL);
comboGrammarVersion.setLayoutData(gd);
//Interpreter
interpreterLabel = new Label(topComp, 0);
interpreterLabel.setText("Interpreter");
gd = new GridData(GridData.FILL_HORIZONTAL);
interpreterLabel.setLayoutData(gd);
//interpreter configured in the project
final String[] idToConfig = new String[] {
"org.python.pydev.ui.pythonpathconf.interpreterPreferencesPagePython" };
interpretersChoice = new Combo(topComp, SWT.READ_ONLY);
selectionListener = new SelectionListener() {
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
/**
* @param e can be null to force an update.
*/
@Override
public void widgetSelected(SelectionEvent e) {
if (e != null) {
Button source = (Button) e.getSource();
if (!source.getSelection()) {
return; //we'll get 2 notifications: selection of one and deselection of the other, so, let's just treat the selection
}
}
IInterpreterManager interpreterManager;
if (radioJy.getSelection()) {
interpreterManager = PydevPlugin.getJythonInterpreterManager();
} else if (radioIron.getSelection()) {
interpreterManager = PydevPlugin.getIronpythonInterpreterManager();
} else {
interpreterManager = PydevPlugin.getPythonInterpreterManager();
}
IInterpreterInfo[] interpretersInfo = interpreterManager.getInterpreterInfos();
if (interpretersInfo.length > 0) {
ArrayList<String> interpretersWithDefault = new ArrayList<String>();
interpretersWithDefault.add(IPythonNature.DEFAULT_INTERPRETER);
for (IInterpreterInfo info : interpretersInfo) {
interpretersWithDefault.add(info.getName());
}
interpretersChoice.setItems(interpretersWithDefault.toArray(new String[0]));
interpretersChoice.setVisible(true);
interpreterNoteText.setText("<a>Click here to configure an interpreter not listed.</a>");
interpretersChoice.setText(IPythonNature.DEFAULT_INTERPRETER);
} else {
interpretersChoice.setVisible(false);
interpreterNoteText.setText(INTERPRETER_NOT_CONFIGURED_MSG);
}
//config which preferences page should be opened!
switch (interpreterManager.getInterpreterType()) {
case IInterpreterManager.INTERPRETER_TYPE_PYTHON:
idToConfig[0] = "org.python.pydev.ui.pythonpathconf.interpreterPreferencesPagePython";
break;
case IInterpreterManager.INTERPRETER_TYPE_JYTHON:
idToConfig[0] = "org.python.pydev.ui.pythonpathconf.interpreterPreferencesPageJython";
break;
case IInterpreterManager.INTERPRETER_TYPE_IRONPYTHON:
idToConfig[0] = "org.python.pydev.ui.pythonpathconf.interpreterPreferencesPageIronpython";
break;
default:
throw new RuntimeException("Cannot recognize type: "
+ interpreterManager.getInterpreterType());
}
triggerCallback();
}
};
gd = new GridData(GridData.FILL_HORIZONTAL);
interpretersChoice.setLayoutData(gd);
radioPy.addSelectionListener(selectionListener);
radioJy.addSelectionListener(selectionListener);
radioIron.addSelectionListener(selectionListener);
interpreterNoteText = new Link(topComp, SWT.LEFT | SWT.WRAP);
gd = new GridData(GridData.FILL_HORIZONTAL);
interpreterNoteText.setLayoutData(gd);
interpreterNoteText.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
String interpreterName = getProjectInterpreter();
if (interpreterName != null) {
PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(
null, idToConfig[0], null, null);
dialog.open();
//just to re-update it again
selectionListener.widgetSelected(null);
} else {
MessageDialog mdialog = new MessageDialog(null, "Configure interpreter", null,
"How would you like to configure the interpreter?", MessageDialog.QUESTION,
InterpreterConfigHelpers.CONFIG_NAMES, 0);
int open = mdialog.open();
if (open == InterpreterConfigHelpers.CONFIG_MANUAL) {
PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(null,
idToConfig[0], null, null);
dialog.open();
//just to re-update it again
selectionListener.widgetSelected(null);
} else if (open != PyDialogHelpers.INTERPRETER_CANCEL_CONFIG) {
//auto-config
InterpreterType interpreterType;
if (radioJy.getSelection()) {
interpreterType = InterpreterType.JYTHON;
} else if (radioIron.getSelection()) {
interpreterType = InterpreterType.IRONPYTHON;
} else {
interpreterType = InterpreterType.PYTHON;
}
JobChangeAdapter onJobComplete = new JobChangeAdapter() {
@Override
public void done(IJobChangeEvent event) {
//Update the display when the configuration has ended.
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
//Only update if the page is still there.
//If something is disposed, it has been closed.
if (!interpreterNoteText.isDisposed()) {
selectionListener.widgetSelected(null);
}
}
});
};
};
interpreterNoteText.setText("Configuration in progress...");
boolean advanced = open == InterpreterConfigHelpers.CONFIG_ADV_AUTO;
AutoConfigMaker a = new AutoConfigMaker(interpreterType, advanced, null, null);
if (a.autoConfigSingleApply(onJobComplete)) {
triggerCallback();
} else {
selectionListener.widgetSelected(null);
}
}
}
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
//Additional grammar validations
Composite composite = new Composite(topComp, SWT.NONE);
composite.setLayout(new GridLayout(2, false));
labelAdditionalGrammarsSelected = new Label(composite, SWT.NONE);
labelAdditionalGrammarsSelected.setText(ADDITIONAL_SYNTAX_NO_SELECTED);
gd = new GridData(GridData.FILL_HORIZONTAL);
labelAdditionalGrammarsSelected.setLayoutData(gd);
Button button = new Button(composite, SWT.PUSH);
button.setText("...");
gd = new GridData();
button.setLayoutData(gd);
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
List<String> grammarversionsrep = new ArrayList<>(IGrammarVersionProvider.grammarVersionsRep);
final String NO_VALIDATION = "No additional syntax validation";
grammarversionsrep.add(NO_VALIDATION);
String[] selected = SelectNDialog.selectMulti(grammarversionsrep,
new LabelProvider(),
"Select additional grammars for syntax validation");
if (ArrayUtils.contains(selected, NO_VALIDATION)) {
selected = null;
}
if (selected == null || selected.length == 0) {
labelAdditionalGrammarsSelected.setText(ADDITIONAL_SYNTAX_NO_SELECTED);
} else {
labelAdditionalGrammarsSelected
.setText(ADDITIONAL_SYNTAX_PREFIX + StringUtils.join(", ", selected));
}
}
});
gd = new GridData(GridData.FILL_HORIZONTAL);
composite.setLayoutData(gd);
return topComp;
}
private void triggerCallback() {
if (onSelectionChanged != null) {
try {
onSelectionChanged.call(null);
} catch (Exception e1) {
Log.log(e1);
}
}
}
/**
* @return a string as specified in the constants in IPythonNature
* @see IPythonNature#PYTHON_VERSION_XXX
* @see IPythonNature#JYTHON_VERSION_XXX
* @see IPythonNature#IRONPYTHON_VERSION_XXX
*/
public String getSelectedPythonOrJythonAndGrammarVersion() {
if (radioPy.getSelection()) {
return "python " + getGrammarVersionSelectedFromCombo();
}
if (radioJy.getSelection()) {
return "jython " + getGrammarVersionSelectedFromCombo();
}
if (radioIron.getSelection()) {
return "ironpython " + getGrammarVersionSelectedFromCombo();
}
throw new RuntimeException("Some radio must be selected");
}
private String getGrammarVersionSelectedFromCombo() {
String ret = comboGrammarVersion.getText();
ret = numberFromUi(ret);
return ret;
}
public String getProjectInterpreter() {
if (INTERPRETER_NOT_CONFIGURED_MSG.equals(interpreterNoteText.getText())) {
return null;
}
return interpretersChoice.getText();
}
public void setDefaultSelection() {
radioPy.setSelection(true);
comboGrammarVersion.setText(numberToUi(IPythonNature.Versions.LAST_VERSION_NUMBER));
//Just to update things
this.selectionListener.widgetSelected(null);
}
public String getAdditionalGrammarValidation() {
String text = labelAdditionalGrammarsSelected.getText();
if (text.equals(ADDITIONAL_SYNTAX_NO_SELECTED)) {
return null;
}
return text.substring(ADDITIONAL_SYNTAX_PREFIX.length());
}
}
static String numberFromUi(String s) {
if ("3.0 - 3.5".equals(s)) {
s = "3.0";
}
return s;
}
static String numberToUi(String s) {
if ("3.0".equals(s)) {
s = "3.0 - 3.5";
}
return s;
}
/**
* The element.
*/
public IAdaptable element;
public ProjectInterpreterAndGrammarConfig projectConfig = new ProjectInterpreterAndGrammarConfig();
/*
* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPropertyPage#getElement()
*/
@Override
public IAdaptable getElement() {
return element;
}
/**
* Sets the element that owns properties shown on this page.
*
* @param element the element
*/
@Override
public void setElement(IAdaptable element) {
this.element = element;
}
public IProject getProject() {
return getElement().getAdapter(IProject.class);
}
@Override
public Control createContents(Composite p) {
Control contents = projectConfig.doCreateContents(p);
setSelected();
return contents;
}
private void setSelected() {
PythonNature pythonNature = PythonNature.getPythonNature(getProject());
try {
//Set whether it's Python/Jython
String version = pythonNature.getVersion();
if (IPythonNature.Versions.ALL_PYTHON_VERSIONS.contains(version)) {
projectConfig.radioPy.setSelection(true);
} else if (IPythonNature.Versions.ALL_IRONPYTHON_VERSIONS.contains(version)) {
projectConfig.radioIron.setSelection(true);
} else if (IPythonNature.Versions.ALL_JYTHON_VERSIONS.contains(version)) {
projectConfig.radioJy.setSelection(true);
}
//We must set the grammar version too (that's from a string in the format "Python 2.4" and we only want
//the version).
String v = StringUtils.split(version, ' ').get(1);
projectConfig.comboGrammarVersion.setText(numberToUi(v));
//Update interpreter
projectConfig.selectionListener.widgetSelected(null);
String configuredInterpreter = pythonNature.getProjectInterpreterName();
if (configuredInterpreter != null) {
projectConfig.interpretersChoice.setText(configuredInterpreter);
}
AdditionalGrammarVersionsToCheck additionalGrammarVersions = null;
try {
additionalGrammarVersions = pythonNature.getAdditionalGrammarVersions();
} catch (MisconfigurationException e) {
}
FastStringBuffer buf = new FastStringBuffer();
if (additionalGrammarVersions != null) {
Set<Integer> grammarVersions = additionalGrammarVersions.getGrammarVersions();
if (grammarVersions != null) {
for (Integer grammarV : new TreeSet<Integer>(grammarVersions)) {
String rep = IGrammarVersionProvider.grammarVersionToRep.get(grammarV);
if (rep != null) {
if (buf.length() > 0) {
buf.append(", ");
}
buf.append(rep);
}
}
}
}
if (buf.length() == 0) {
projectConfig.labelAdditionalGrammarsSelected.setText(ADDITIONAL_SYNTAX_NO_SELECTED);
} else {
projectConfig.labelAdditionalGrammarsSelected.setText(ADDITIONAL_SYNTAX_PREFIX + buf.toString());
}
} catch (CoreException e) {
Log.log(e);
}
}
@Override
protected void performApply() {
doIt();
}
@Override
public boolean performOk() {
return doIt();
}
@Override
public boolean performCancel() {
//re-enable "configure interpreter" dialogs
PyDialogHelpers.enableAskInterpreterStep(true);
return super.performCancel();
}
private boolean doIt() {
IProject project = getProject();
if (project != null) {
PythonNature pythonNature = PythonNature.getPythonNature(project);
try {
String projectInterpreter = projectConfig.getProjectInterpreter();
if (projectInterpreter == null) {
return false;
}
pythonNature.setVersion(projectConfig.getSelectedPythonOrJythonAndGrammarVersion(), projectInterpreter);
pythonNature.setAdditionalGrammarValidation(projectConfig.getAdditionalGrammarValidation());
} catch (CoreException e) {
Log.log(e);
}
}
//re-enable "configure interpreter" dialogs
PyDialogHelpers.enableAskInterpreterStep(true);
return true;
}
}