initial 2-step create new project dialog in PyCharm

This commit is contained in:
Ekaterina Tuzova
2014-06-11 21:58:46 +04:00
parent ba0cb96a6d
commit 3721c14cac
12 changed files with 900 additions and 11 deletions
@@ -0,0 +1,27 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.newProject;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
public class PyCharmNewProjectAction extends AnAction {
public void actionPerformed(final AnActionEvent e) {
final PyCharmNewProjectDialog dlg = new PyCharmNewProjectDialog();
dlg.show();
}
}
@@ -0,0 +1,70 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.newProject;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.wm.impl.welcomeScreen.CardActionsPanel;
import com.jetbrains.python.newProject.actions.PyCharmNewProjectStep;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
public class PyCharmNewProjectDialog extends DialogWrapper {
public PyCharmNewProjectDialog() {
super(ProjectManager.getInstance().getDefaultProject());
setTitle(null);
init();
}
@Nullable
@Override
protected JComponent createCenterPanel() {
final Runnable runnable = new Runnable() {
@Override
public void run() {
PyCharmNewProjectDialog.this.close(OK_EXIT_CODE);
}
};
final DefaultActionGroup root = new PyCharmNewProjectStep(runnable);
return new CardActionsPanel(root) {
@Override
public Dimension getPreferredSize() {
return getMinimumSize();
}
@Override
public Dimension getMinimumSize() {
return new Dimension(800, 500);
}
};
}
@Override
protected String getHelpId() {
return null;
}
@NotNull
protected Action[] createActions() {
return new Action[0];
}
}
@@ -0,0 +1,79 @@
package com.jetbrains.python.newProject;
import com.intellij.facet.ui.ValidationResult;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.platform.DirectoryProjectGenerator;
import icons.PythonIcons;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.File;
public class PythonBaseProjectGenerator implements DirectoryProjectGenerator {
@NotNull
@Nls
@Override
public String getName() {
return "Basic project";
}
@Nullable
@Override
public Object showGenerationSettings(VirtualFile baseDir) throws ProcessCanceledException {
return null;
}
@Override
@Nullable
public JComponent getSettingsPanel(File baseDir) throws ProcessCanceledException {
return null;
}
@Override
public Object getProjectSettings() throws ProcessCanceledException {
return new PyNewProjectSettings();
}
@Nullable
@Override
public Icon getIcon() {
return PythonIcons.Python.Python_logo;
}
@Override
public void generateProject(@NotNull final Project project, @NotNull VirtualFile baseDir, final Object settings,
@NotNull final Module module) {
/*if (settings instanceof RemoteProjectSettings) {
PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
assert manager != null;
manager.createDeployment(project, baseDir, (RemoteProjectSettings)settings,
(RemoteSdkCredentials)myProjectAction.getSdk().getSdkAdditionalData());
}
else */if (settings instanceof PyNewProjectSettings) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
ModuleRootModificationUtil.setModuleSdk(module, ((PyNewProjectSettings)settings).getSdk());
}
});
}
}
@NotNull
@Override
public ValidationResult validate(@NotNull String baseDirPath) {
/*if (PythonSdkType.isRemote(myProjectAction.getSdk())) {
if (PythonRemoteInterpreterManager.getInstance() == null) {
return new ValidationResult(PythonRemoteInterpreterManager.WEB_DEPLOYMENT_PLUGIN_IS_DISABLED);
}
}*/
return ValidationResult.OK;
}
}
@@ -54,6 +54,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.List;
/**
@@ -179,6 +180,24 @@ public class PythonNewDirectoryProjectDialog extends NewDirectoryProjectDialog {
}
}
@Nullable
@Override
public JComponent getSettingsPanel(File baseDir) throws ProcessCanceledException {
return null;
}
@Nullable
@Override
public Object getProjectSettings() throws ProcessCanceledException {
return null;
}
@Nullable
@Override
public Icon getIcon() {
return null;
}
@Override
public void generateProject(@NotNull final Project project,
@NotNull VirtualFile baseDir,
@@ -283,7 +302,7 @@ public class PythonNewDirectoryProjectDialog extends NewDirectoryProjectDialog {
private boolean isFrameworkInstalled(Sdk sdk) {
PyFrameworkProjectGenerator projectGenerator = (PyFrameworkProjectGenerator)getProjectGenerator();
return projectGenerator != null && projectGenerator.isFrameworkInstalled(myProject, sdk);
return projectGenerator != null && projectGenerator.isFrameworkInstalled(sdk);
}
private static boolean acceptsRemoteSdk(DirectoryProjectGenerator generator) {
@@ -0,0 +1,401 @@
package com.jetbrains.python.newProject.actions;
import com.intellij.facet.ui.ValidationResult;
import com.intellij.ide.impl.ProjectUtil;
import com.intellij.ide.util.projectWizard.WebProjectTemplate;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.actionSystem.impl.ActionButtonWithText;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.ui.ValidationInfo;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.wm.impl.welcomeScreen.AbstractActionWithPanel;
import com.intellij.platform.DirectoryProjectGenerator;
import com.intellij.platform.WebProjectGenerator;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.util.NullableConsumer;
import com.intellij.util.ui.CenteredIcon;
import com.jetbrains.python.PythonSdkChooserCombo;
import com.jetbrains.python.configuration.PyConfigurableInterpreterList;
import com.jetbrains.python.configuration.VirtualEnvProjectFilter;
import com.jetbrains.python.newProject.PyFrameworkProjectGenerator;
import com.jetbrains.python.packaging.PyExternalProcessException;
import com.jetbrains.python.packaging.PyPackage;
import com.jetbrains.python.packaging.PyPackageManager;
import com.jetbrains.python.packaging.PyPackageManagerImpl;
import com.jetbrains.python.sdk.PythonSdkType;
import com.jetbrains.python.sdk.flavors.JythonSdkFlavor;
import com.jetbrains.python.sdk.flavors.PyPySdkFlavor;
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
import icons.PythonIcons;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.List;
abstract public class AbstractProjectSettingsStep extends AbstractActionWithPanel implements DumbAware {
protected final DirectoryProjectGenerator myProjectGenerator;
private final NullableConsumer<AbstractProjectSettingsStep> myCallback;
private PythonSdkChooserCombo mySdkCombo;
private JCheckBox myFrameworkCheckbox;
private boolean myInstallFrameworkChanged;
private TextFieldWithBrowseButton myLocationField;
protected final File myProjectDirectory;
private Button myCreateButton;
private JLabel myErrorLabel;
private AnAction myCreateAction;
public AbstractProjectSettingsStep(DirectoryProjectGenerator projectGenerator, NullableConsumer<AbstractProjectSettingsStep> callback) {
super();
myProjectGenerator = projectGenerator;
myCallback = callback;
myProjectDirectory = FileUtil.findSequentNonexistentFile(new File(ProjectUtil.getBaseDir()), "untitled", "");
if (myProjectGenerator instanceof WebProjectTemplate) {
((WebProjectTemplate)myProjectGenerator).getPeer().addSettingsStateListener(new WebProjectGenerator.SettingsStateListener() {
@Override
public void stateChanged(boolean validSettings) {
if (validSettings) {
setErrorText(null);
}
}
});
}
myCreateAction = new AnAction("Create ", "Create Project", getIcon()) {
@Override
public void actionPerformed(AnActionEvent e) {
boolean isOk;
try {
isOk = checkValid();
if (myProjectGenerator instanceof WebProjectTemplate) {
final ValidationInfo validationInfo = ((WebProjectTemplate)myProjectGenerator).getPeer().validate();
isOk = validationInfo == null;
if (!isOk) {
setErrorText(validationInfo.message);
}
}
}
catch (RuntimeException e1) {
isOk = false;
setErrorText(e1.getMessage());
}
if (isOk && myCallback != null)
myCallback.consume(AbstractProjectSettingsStep.this);
}
};
}
@Override
public void actionPerformed(AnActionEvent e) {
}
@Override
public JPanel createPanel() {
final JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.setPreferredSize(new Dimension(mainPanel.getPreferredSize().width, 450));
final JPanel panel = createBasePanel();
mainPanel.add(panel, BorderLayout.NORTH);
final JPanel advancedSettings = createAdvancedSettings();
if (advancedSettings != null)
mainPanel.add(advancedSettings, BorderLayout.CENTER);
final JPanel bottomPanel = new JPanel(new BorderLayout());
myCreateButton = new Button(myCreateAction, myCreateAction.getTemplatePresentation());
bottomPanel.setPreferredSize(new Dimension(mainPanel.getPreferredSize().width, 40));
myCreateButton.setPreferredSize(new Dimension(mainPanel.getPreferredSize().width, 40));
myErrorLabel = new JLabel("");
myErrorLabel.setForeground(JBColor.RED);
bottomPanel.add(myErrorLabel, BorderLayout.WEST);
bottomPanel.add(myCreateButton, BorderLayout.CENTER);
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
return mainPanel;
}
protected Icon getIcon() {
return myProjectGenerator.getIcon();
}
private JPanel createBasePanel() {
final JPanel panel = new JPanel(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.NORTHWEST;
c.weightx = 0;
c.insets = new Insets(2, 2, 2, 2);
myLocationField = new TextFieldWithBrowseButton();
myLocationField.setText(myProjectDirectory.toString());
final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
myLocationField.addBrowseFolderListener("Select base directory", "Select base directory for the Project",
null, descriptor);
final JLabel locationLabel = new JLabel("Location:");
c.gridx = 0;
c.gridy = 0;
panel.add(locationLabel, c);
c.gridx = 1;
c.gridy = 0;
c.weightx = 1.;
panel.add(myLocationField, c);
final JLabel interpreterLabel = new JLabel("Interpreter:", SwingConstants.LEFT) {
@Override
public Dimension getMinimumSize() {
return new JLabel("Project name:").getPreferredSize();
}
@Override
public Dimension getPreferredSize() {
return getMinimumSize();
}
};
c.gridx = 0;
c.gridy = 1;
c.weightx = 0;
panel.add(interpreterLabel, c);
final Project project = ProjectManager.getInstance().getDefaultProject();
final List<Sdk> sdks = PyConfigurableInterpreterList.getInstance(project).getAllPythonSdks();
VirtualEnvProjectFilter.removeAllAssociated(sdks);
final Sdk preferred = sdks.isEmpty() ? null : sdks.iterator().next();
mySdkCombo = new PythonSdkChooserCombo(project, sdks, new Condition<Sdk>() {
@Override
public boolean value(Sdk sdk) {
return sdk == preferred;
}
});
mySdkCombo.setButtonIcon(PythonIcons.Python.InterpreterGear);
c.gridx = 1;
c.gridy = 1;
c.weightx = 1.;
panel.add(mySdkCombo, c);
myFrameworkCheckbox = new JBCheckBox("Install <framework>");
c.gridx = 0;
c.gridy = 2;
c.gridwidth = 2;
c.weightx = 0.0;
panel.add(myFrameworkCheckbox, c);
myFrameworkCheckbox.setVisible(false);
registerValidators();
return panel;
}
protected void registerValidators() {
myFrameworkCheckbox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
myInstallFrameworkChanged = true;
checkValid();
}
});
myLocationField.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
checkValid();
}
});
final ActionListener listener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
checkValid();
}
};
mySdkCombo.getComboBox().addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
checkValid();
}
});
myLocationField.getTextField().addActionListener(listener);
mySdkCombo.getComboBox().addActionListener(listener);
mySdkCombo.addActionListener(listener);
}
public boolean checkValid() {
final String projectName = myLocationField.getText();
if (projectName.trim().isEmpty()) {
setErrorText("Project name can't be empty");
return false;
}
if (myLocationField.getText().indexOf('$') >= 0) {
setErrorText("Project directory name must not contain the $ character");
return false;
}
if (myProjectGenerator != null) {
final String baseDirPath = myLocationField.getTextField().getText();
ValidationResult validationResult = myProjectGenerator.validate(baseDirPath);
if (!validationResult.isOk()) {
setErrorText(validationResult.getErrorMessage());
return false;
}
if (myProjectGenerator instanceof WebProjectTemplate) {
final ValidationInfo validationInfo = ((WebProjectTemplate)myProjectGenerator).getPeer().validate();
if (validationInfo != null) {
setErrorText(validationInfo.message);
return false;
}
}
}
final Sdk sdk = getSdk();
setErrorText(null);
myFrameworkCheckbox.setVisible(false);
final boolean isPy3k = sdk != null && PythonSdkType.getLanguageLevelForSdk(sdk).isPy3K();
if (sdk != null && PythonSdkType.isRemote(sdk) && !acceptsRemoteSdk(myProjectGenerator)) {
setErrorText("Please choose a local interpreter");
return false;
}
else if (myProjectGenerator instanceof PyFrameworkProjectGenerator) {
PyFrameworkProjectGenerator frameworkProjectGenerator = (PyFrameworkProjectGenerator)myProjectGenerator;
String frameworkName = frameworkProjectGenerator.getFrameworkTitle();
if (sdk != null && !isFrameworkInstalled(sdk)) {
final PyPackageManagerImpl packageManager = (PyPackageManagerImpl)PyPackageManager.getInstance(sdk);
final boolean onlyWithCache =
PythonSdkFlavor.getFlavor(sdk) instanceof JythonSdkFlavor || PythonSdkFlavor.getFlavor(sdk) instanceof PyPySdkFlavor;
try {
if (onlyWithCache && packageManager.cacheIsNotNull() || !onlyWithCache) {
final PyPackage pip = packageManager.findPackage("pip");
myFrameworkCheckbox.setText("Install " + frameworkName);
myFrameworkCheckbox.setMnemonic(frameworkName.charAt(0));
myFrameworkCheckbox.setVisible(pip != null);
if (!myInstallFrameworkChanged) {
myFrameworkCheckbox.setSelected(pip != null);
}
}
}
catch (PyExternalProcessException e) {
myFrameworkCheckbox.setVisible(false);
}
if (!myFrameworkCheckbox.isSelected()) {
setErrorText("No " + frameworkName + " support installed in selected interpreter");
return false;
}
}
if (isPy3k && !((PyFrameworkProjectGenerator)myProjectGenerator).supportsPython3()) {
setErrorText(frameworkName + " is not supported for the selected interpreter");
return false;
}
}
if (sdk == null) {
setErrorText("No Python interpreter selected");
return false;
}
return true;
}
public void setErrorText(@Nullable String text) {
myErrorLabel.setText(text);
myCreateButton.setEnabled(text == null);
}
public void selectCompatiblePython() {
DirectoryProjectGenerator generator = getProjectGenerator();
if (generator instanceof PyFrameworkProjectGenerator && !((PyFrameworkProjectGenerator)generator).supportsPython3()) {
Sdk sdk = getSdk();
if (sdk != null && PythonSdkType.getLanguageLevelForSdk(sdk).isPy3K()) {
Sdk python2Sdk = PythonSdkType.findPython2Sdk(null);
if (python2Sdk != null) {
mySdkCombo.getComboBox().setSelectedItem(python2Sdk);
mySdkCombo.getComboBox().repaint();
}
}
}
}
private static boolean acceptsRemoteSdk(DirectoryProjectGenerator generator) {
if (generator instanceof PyFrameworkProjectGenerator) {
return ((PyFrameworkProjectGenerator)generator).acceptsRemoteSdk();
}
return true;
}
private boolean isFrameworkInstalled(Sdk sdk) {
PyFrameworkProjectGenerator projectGenerator = (PyFrameworkProjectGenerator)getProjectGenerator();
return projectGenerator != null && projectGenerator.isFrameworkInstalled(sdk);
}
@Nullable
protected JPanel createAdvancedSettings() {
return null;
}
public DirectoryProjectGenerator getProjectGenerator() {
return myProjectGenerator;
}
private static class Button extends ActionButtonWithText {
private static final Icon DEFAULT_ICON = PythonIcons.Python.Python;
public Button(AnAction action, Presentation presentation) {
super(action,
wrapIcon(presentation),
"NewProject",
new Dimension(32, 32));
setBorder(new EmptyBorder(3, 3, 3, 3));
}
@Override
public String getToolTipText() {
return null;
}
@Override
protected int horizontalTextAlignment() {
return SwingConstants.RIGHT;
}
@Override
protected int iconTextSpace() {
return 8;
}
private static Presentation wrapIcon(Presentation presentation) {
Icon original = presentation.getIcon();
CenteredIcon centered = new CenteredIcon(original != null ? original : DEFAULT_ICON, 40, 40, false);
presentation.setIcon(centered);
return presentation;
}
}
public Sdk getSdk() {
return (Sdk)mySdkCombo.getComboBox().getSelectedItem();
}
public String getProjectLocation() {
return myLocationField.getText();
}
public boolean installFramework() {
return myFrameworkCheckbox.isSelected() && myFrameworkCheckbox.isVisible();
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.newProject.actions;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.platform.DirectoryProjectGenerator;
import com.intellij.util.NullableConsumer;
import org.jetbrains.annotations.NotNull;
public class ProjectSpecificAction extends DefaultActionGroup implements DumbAware {
private final ProjectSpecificSettingsStep mySettings;
public ProjectSpecificAction(@NotNull final NullableConsumer<AbstractProjectSettingsStep> callback,
@NotNull final DirectoryProjectGenerator projectGenerator) {
super(projectGenerator.getName(), true);
getTemplatePresentation().setIcon(projectGenerator.getIcon());
mySettings = new ProjectSpecificSettingsStep(projectGenerator, callback);
add(mySettings);
}
public Sdk getSdk() {
return mySettings.getSdk();
}
@Override
public void actionPerformed(AnActionEvent e) {
super.actionPerformed(e);
mySettings.selectCompatiblePython();
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.newProject.actions;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.ui.VerticalFlowLayout;
import com.intellij.platform.DirectoryProjectGenerator;
import com.intellij.ui.HideableDecorator;
import com.intellij.util.NullableConsumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
public class ProjectSpecificSettingsStep extends AbstractProjectSettingsStep implements DumbAware {
public ProjectSpecificSettingsStep(@NotNull final DirectoryProjectGenerator projectGenerator,
@NotNull final NullableConsumer<AbstractProjectSettingsStep> callback) {
super(projectGenerator, callback);
}
@Override
@Nullable
protected JPanel createAdvancedSettings() {
final JComponent advancedSettings = myProjectGenerator.getSettingsPanel(myProjectDirectory);
if (advancedSettings != null) {
final JPanel jPanel = new JPanel(new VerticalFlowLayout());
final HideableDecorator deco = new HideableDecorator(jPanel, "Mor&e Settings", false);
deco.setContentComponent(advancedSettings);
return jPanel;
}
return null;
}
}
@@ -0,0 +1,178 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.newProject.actions;
import com.intellij.ide.GeneralSettings;
import com.intellij.internal.statistic.UsageTrigger;
import com.intellij.internal.statistic.beans.ConvertUsagesUtil;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.projectRoots.ProjectJdkTable;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkAdditionalData;
import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.platform.DirectoryProjectGenerator;
import com.intellij.platform.PlatformProjectOpenProcessor;
import com.intellij.projectImport.ProjectOpenedCallback;
import com.intellij.util.NullableConsumer;
import com.jetbrains.python.configuration.PyConfigurableInterpreterList;
import com.jetbrains.python.newProject.PyFrameworkProjectGenerator;
import com.jetbrains.python.newProject.PyNewProjectSettings;
import com.jetbrains.python.newProject.PythonBaseProjectGenerator;
import com.jetbrains.python.sdk.PyDetectedSdk;
import com.jetbrains.python.sdk.PySdkService;
import com.jetbrains.python.sdk.PythonSdkAdditionalData;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class PyCharmNewProjectStep extends DefaultActionGroup implements DumbAware {
private static final Logger LOG = Logger.getInstance(PyCharmNewProjectStep.class);
public PyCharmNewProjectStep(@Nullable final Runnable runnable) {
super("Select Project Type", true);
final NullableConsumer<AbstractProjectSettingsStep> callback = new NullableConsumer<AbstractProjectSettingsStep>() {
@Override
public void consume(@Nullable AbstractProjectSettingsStep settingsStep) {
if (runnable != null)
runnable.run();
if (settingsStep == null) return;
Sdk sdk = settingsStep.getSdk();
final Project project = ProjectManager.getInstance().getDefaultProject();
final ProjectSdksModel model = PyConfigurableInterpreterList.getInstance(project).getModel();
if (sdk instanceof PyDetectedSdk) {
final String name = sdk.getName();
VirtualFile sdkHome = ApplicationManager.getApplication().runWriteAction(new Computable<VirtualFile>() {
@Override
public VirtualFile compute() {
return LocalFileSystem.getInstance().refreshAndFindFileByPath(name);
}
});
PySdkService.getInstance().solidifySdk(sdk);
sdk = SdkConfigurationUtil.setupSdk(ProjectJdkTable.getInstance().getAllJdks(), sdkHome, PythonSdkType.getInstance(), true, null,
null);
model.addSdk(sdk);
try {
model.apply();
}
catch (ConfigurationException exception) {
LOG.error("Error adding detected python interpreter " + exception.getMessage());
}
}
Project newProject = generateProject(project, settingsStep);
if (newProject != null) {
SdkConfigurationUtil.setDirectoryProjectSdk(newProject, sdk);
final List<Sdk> sdks = PythonSdkType.getAllSdks();
for (Sdk s : sdks) {
final SdkAdditionalData additionalData = s.getSdkAdditionalData();
if (additionalData instanceof PythonSdkAdditionalData) {
((PythonSdkAdditionalData)additionalData).reassociateWithCreatedProject(newProject);
}
}
}
}
@Nullable
private Project generateProject(@NotNull final Project project, @NotNull final AbstractProjectSettingsStep settings) {
final DirectoryProjectGenerator generator = settings.getProjectGenerator();
final File location = new File(settings.getProjectLocation());
if (!location.exists() && !location.mkdirs()) {
Messages.showErrorDialog(project, "Cannot create directory '" + location + "'", "Create Project");
return null;
}
final VirtualFile baseDir = ApplicationManager.getApplication().runWriteAction(new Computable<VirtualFile>() {
public VirtualFile compute() {
return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(location);
}
});
LOG.assertTrue(baseDir != null, "Couldn't find '" + location + "' in VFS");
baseDir.refresh(false, true);
if (baseDir.getChildren().length > 0) {
int rc = Messages.showYesNoDialog(project,
"The directory '" + location +
"' is not empty. Would you like to create a project from existing sources instead?",
"Create New Project", Messages.getQuestionIcon());
if (rc == Messages.YES) {
return PlatformProjectOpenProcessor.getInstance().doOpenProject(baseDir, null, false);
}
}
String generatorName = generator == null ? "empty" : ConvertUsagesUtil.ensureProperKey(generator.getName());
UsageTrigger.trigger("NewDirectoryProjectAction." + generatorName);
GeneralSettings.getInstance().setLastProjectCreationLocation(location.getParent());
return PlatformProjectOpenProcessor.doOpenProject(baseDir, null, false, -1, new ProjectOpenedCallback() {
@Override
public void projectOpened(Project project, Module module) {
if (generator != null) {
final Object projectSettings = generator.getProjectSettings();
if (projectSettings instanceof PyNewProjectSettings) {
((PyNewProjectSettings)projectSettings).setSdk(settings.getSdk());
((PyNewProjectSettings)projectSettings).setInstallFramework(settings.installFramework());
}
//noinspection unchecked
generator.generateProject(project, baseDir, projectSettings, module);
}
}
}, false);
}
};
final ProjectSpecificAction action = new ProjectSpecificAction(callback, new PythonBaseProjectGenerator());
add(action);
final DirectoryProjectGenerator[] generators = Extensions.getExtensions(DirectoryProjectGenerator.EP_NAME);
Arrays.sort(generators, new Comparator<DirectoryProjectGenerator>() {
@Override
public int compare(DirectoryProjectGenerator o1, DirectoryProjectGenerator o2) {
if (o1 instanceof PyFrameworkProjectGenerator && !(o2 instanceof PyFrameworkProjectGenerator)) return -1;
if (!(o1 instanceof PyFrameworkProjectGenerator) && o2 instanceof PyFrameworkProjectGenerator) return 1;
return o1.getName().compareTo(o2.getName());
}
});
for (DirectoryProjectGenerator generator : generators) {
add(new ProjectSpecificAction(callback, generator));
}
}
public PyCharmNewProjectStep() {
this(null);
}
}