extract common stuff for inline new project wizard

This commit is contained in:
Dennis Ushakov
2014-12-05 12:47:07 +03:00
parent c0e7b2bbc4
commit 8cdaf1c6a2
12 changed files with 629 additions and 661 deletions
@@ -15,60 +15,12 @@
*/
package com.jetbrains.python.newProject;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.wm.impl.welcomeScreen.CardActionsPanel;
import com.intellij.platform.DirectoryProjectGenerator;
import com.intellij.ide.util.projectWizard.AbstractNewProjectDialog;
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(" "); // hack to make native fileChooser work on Mac. See MacFileChooserDialogImpl.MAIN_THREAD_RUNNABLE
init();
}
@Nullable
public class PyCharmNewProjectDialog extends AbstractNewProjectDialog {
@Override
protected JComponent createCenterPanel() {
final Runnable runnable = new Runnable() {
@Override
public void run() {
PyCharmNewProjectDialog.this.close(OK_EXIT_CODE);
}
};
final DirectoryProjectGenerator[] generators = Extensions.getExtensions(DirectoryProjectGenerator.EP_NAME);
final DefaultActionGroup root = new PyCharmNewProjectStep(generators.length == 0 ? "Create Project" : "Select Project Type", runnable);
return new CardActionsPanel(root) {
@Override
public Dimension getPreferredSize() {
return getMinimumSize();
}
@Override
public Dimension getMinimumSize() {
if (generators.length == 0) return new Dimension(550, 200);
return new Dimension(650, 450);
}
};
}
@Override
protected String getHelpId() {
return null;
}
@NotNull
protected Action[] createActions() {
return new Action[0];
protected PyCharmNewProjectStep createRootStep(Runnable runnable, final String name) {
return new PyCharmNewProjectStep(name, runnable);
}
}
@@ -1,484 +0,0 @@
/*
* Copyright 2000-2014 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.execution.ExecutionException;
import com.intellij.facet.ui.ValidationResult;
import com.intellij.icons.AllIcons;
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.extensions.Extensions;
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.DialogWrapper;
import com.intellij.openapi.ui.MessageType;
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.JBScrollPane;
import com.intellij.util.NullableConsumer;
import com.intellij.util.ui.UIUtil;
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.newProject.PythonProjectGenerator;
import com.jetbrains.python.packaging.PyPackageManager;
import com.jetbrains.python.sdk.PySdkUtil;
import com.jetbrains.python.sdk.PythonSdkType;
import icons.PythonIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.MouseEvent;
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 final boolean myIsWelcomeScreen;
private PythonSdkChooserCombo mySdkCombo;
private boolean myInstallFramework;
private TextFieldWithBrowseButton myLocationField;
protected final File myProjectDirectory;
private JButton myCreateButton;
private JLabel myErrorLabel;
private AnAction myCreateAction;
private Sdk mySdk;
public AbstractProjectSettingsStep(DirectoryProjectGenerator projectGenerator,
NullableConsumer<AbstractProjectSettingsStep> callback,
boolean isWelcomeScreen) {
super();
getTemplatePresentation().setIcon(projectGenerator.getLogo());
getTemplatePresentation().setText(projectGenerator.getName());
myProjectGenerator = projectGenerator;
myCallback = callback;
myIsWelcomeScreen = isWelcomeScreen;
myProjectDirectory = FileUtil.findSequentNonexistentFile(new File(ProjectUtil.getBaseDir()), "untitled", "");
myCreateAction = new AnAction("Create", "Create Project", getIcon()) {
@Override
public void actionPerformed(AnActionEvent e) {
boolean isValid = checkValid();
if (isValid && myCallback != null)
myCallback.consume(AbstractProjectSettingsStep.this);
}
};
}
@Override
public void actionPerformed(AnActionEvent e) {
}
@Override
public JPanel createPanel() {
initGeneratorListeners();
final JPanel basePanel = createBasePanel();
final JPanel mainPanel = new JPanel(new BorderLayout());
final JPanel scrollPanel = new JPanel(new BorderLayout());
final DirectoryProjectGenerator[] generators = Extensions.getExtensions(DirectoryProjectGenerator.EP_NAME);
final int height = generators.length == 0 && !myIsWelcomeScreen ? 150 : 400;
//mainPanel.setPreferredSize(new Dimension(mainPanel.getPreferredSize().width, height));
myErrorLabel = new JLabel("");
myErrorLabel.setForeground(JBColor.RED);
myCreateButton = new JButton("Create");
myCreateButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
boolean isValid = checkValid();
if (isValid && myCallback != null) {
myCallback.consume(AbstractProjectSettingsStep.this);
}
}
});
myCreateButton.putClientProperty(DialogWrapper.DEFAULT_ACTION, Boolean.TRUE);
scrollPanel.add(basePanel, BorderLayout.NORTH);
final JPanel advancedSettings = createAdvancedSettings();
if (advancedSettings != null) {
scrollPanel.add(advancedSettings, BorderLayout.CENTER);
}
final JBScrollPane scrollPane = new JBScrollPane(scrollPanel, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setBorder(null);
mainPanel.add(scrollPane, BorderLayout.CENTER);
final JPanel bottomPanel = new JPanel(new BorderLayout());
bottomPanel.add(myErrorLabel, BorderLayout.NORTH);
bottomPanel.add(myCreateButton, BorderLayout.EAST);
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
return mainPanel;
}
private void initGeneratorListeners() {
if (myProjectGenerator instanceof WebProjectTemplate) {
((WebProjectTemplate)myProjectGenerator).getPeer().addSettingsStateListener(new WebProjectGenerator.SettingsStateListener() {
@Override
public void stateChanged(boolean validSettings) {
checkValid();
}
});
}
else if (myProjectGenerator instanceof PythonProjectGenerator) {
((PythonProjectGenerator)myProjectGenerator).addSettingsStateListener(new PythonProjectGenerator.SettingsListener() {
@Override
public void stateChanged() {
checkValid();
}
});
}
}
protected Icon getIcon() {
return myProjectGenerator.getLogo();
}
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);
myLocationField.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
if (myProjectGenerator instanceof PythonProjectGenerator) {
String path = myLocationField.getText().trim();
if (path.endsWith(File.separator)) {
path = path.substring(0, path.length() - File.separator.length());
}
int ind = path.lastIndexOf(File.separator);
if (ind != -1) {
String projectName = path.substring(ind + 1, path.length());
((PythonProjectGenerator)myProjectGenerator).locationChanged(projectName);
}
}
}
});
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);
Sdk compatibleSdk = sdks.isEmpty() ? null : sdks.iterator().next();
DirectoryProjectGenerator generator = getProjectGenerator();
if (generator instanceof PyFrameworkProjectGenerator && !((PyFrameworkProjectGenerator)generator).supportsPython3()) {
if (compatibleSdk != null && PythonSdkType.getLanguageLevelForSdk(compatibleSdk).isPy3K()) {
Sdk python2Sdk = PythonSdkType.findPython2Sdk(sdks);
if (python2Sdk != null) {
compatibleSdk = python2Sdk;
}
}
}
final Sdk preferred = compatibleSdk;
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);
final JPanel basePanelExtension = extendBasePanel();
if (basePanelExtension != null) {
c.gridwidth = 2;
c.gridy = 2;
c.gridx = 0;
panel.add(basePanelExtension, c);
}
registerValidators();
return panel;
}
@Nullable
protected JPanel extendBasePanel() {
if (myProjectGenerator instanceof PythonProjectGenerator)
return ((PythonProjectGenerator)myProjectGenerator).extendBasePanel();
return null;
}
protected void registerValidators() {
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() {
if (myLocationField == null) return true;
final String projectName = myLocationField.getText();
setErrorText(null);
myInstallFramework = false;
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 PythonProjectGenerator) {
final ValidationResult warningResult = ((PythonProjectGenerator)myProjectGenerator).warningValidation(getSdk());
if (!warningResult.isOk()) {
setWarningText(warningResult.getErrorMessage());
}
}
if (myProjectGenerator instanceof WebProjectTemplate) {
final WebProjectGenerator.GeneratorPeer peer = ((WebProjectTemplate)myProjectGenerator).getPeer();
final ValidationInfo validationInfo = peer.validate();
if (validationInfo != null && !peer.isBackgroundJobRunning()) {
setErrorText(validationInfo.message);
return false;
}
}
}
final Sdk sdk = getSdk();
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)) {
String warningText = frameworkName + " will be installed on selected interpreter";
myInstallFramework = true;
final PyPackageManager packageManager = PyPackageManager.getInstance(sdk);
boolean hasManagement = false;
try {
hasManagement = packageManager.hasManagement(PySdkUtil.isRemote(sdk));
}
catch (ExecutionException ignored) {
}
if (!hasManagement) {
warningText = "Python packaging tools and " + warningText;
}
setWarningText(warningText);
}
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);
myErrorLabel.setForeground(MessageType.ERROR.getTitleForeground());
myErrorLabel.setIcon(text == null ? null : AllIcons.Actions.Lightning);
myCreateButton.setEnabled(text == null);
}
public void setWarningText(@Nullable String text) {
myErrorLabel.setText("Note: " + text + " ");
myErrorLabel.setForeground(MessageType.WARNING.getTitleForeground());
}
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 final Border myBorder;
public Button(AnAction action, Presentation presentation) {
super(action, presentation, "NewProject", new Dimension(70, 50));
final Border border = new LineBorder(JBColor.border(), 1, true);
myBorder = UIUtil.isUnderDarcula() ? UIUtil.getButtonBorder() : border;
setBorder(myBorder);
}
@Override
protected int iconTextSpace() {
return 8;
}
@Override
public boolean isFocusable() {
return true;
}
@Override
protected void processFocusEvent(FocusEvent e) {
super.processFocusEvent(e);
if (e.getID() == FocusEvent.FOCUS_GAINED) {
processMouseEvent(new MouseEvent(this, MouseEvent.MOUSE_ENTERED, System.currentTimeMillis(), 0, 0, 0, 0, false));
}
else if (e.getID() == FocusEvent.FOCUS_LOST) {
processMouseEvent(new MouseEvent(this, MouseEvent.MOUSE_EXITED, System.currentTimeMillis(), 0, 0, 0, 0, false));
}
}
@Override
public Insets getInsets() {
return new Insets(5,10,5,5);
}
@Override
protected int horizontalTextAlignment() {
return SwingConstants.LEFT;
}
protected void processMouseEvent(MouseEvent e) {
super.processMouseEvent(e);
if (e.getID() == MouseEvent.MOUSE_ENTERED) {
setBorder(null);
}
else if (e.getID() == MouseEvent.MOUSE_EXITED) {
setBorder(myBorder);
}
}
}
public Sdk getSdk() {
if (mySdk != null) return mySdk;
return (Sdk)mySdkCombo.getComboBox().getSelectedItem();
}
public void setSdk(final Sdk sdk) {
mySdk = sdk;
}
public String getProjectLocation() {
return myLocationField.getText();
}
public void setLocation(@NotNull final String location) {
myLocationField.setText(location);
}
public boolean installFramework() {
return myInstallFramework;
}
}
@@ -15,13 +15,10 @@
*/
package com.jetbrains.python.newProject.actions;
import com.intellij.ide.RecentProjectsManager;
import com.intellij.ide.util.projectWizard.ProjectSettingsStepBase;
import com.intellij.ide.util.projectWizard.WebProjectTemplate;
import com.intellij.internal.statistic.UsageTrigger;
import com.intellij.internal.statistic.beans.ConvertUsagesUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
@@ -30,13 +27,12 @@ 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.platform.NewDirectoryProjectAction;
import com.intellij.util.Function;
import com.intellij.util.NullableConsumer;
import com.jetbrains.python.configuration.PyConfigurableInterpreterList;
import com.jetbrains.python.newProject.PyNewProjectSettings;
@@ -48,24 +44,24 @@ import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.List;
public class GenerateProjectCallback implements NullableConsumer<AbstractProjectSettingsStep> {
public class GenerateProjectCallback implements NullableConsumer<ProjectSettingsStepBase> {
private static final Logger LOG = Logger.getInstance(GenerateProjectCallback.class);
@Nullable private final Runnable myRunnable;
public GenerateProjectCallback(@Nullable final Runnable runnable) {
myRunnable = runnable;
}
@Override
public void consume(@Nullable AbstractProjectSettingsStep settingsStep) {
public void consume(@Nullable ProjectSettingsStepBase step) {
if (myRunnable != null) {
myRunnable.run();
}
if (settingsStep == null) return;
if (!(step instanceof ProjectSpecificSettingsStep)) return;
final ProjectSpecificSettingsStep settingsStep = (ProjectSpecificSettingsStep)step;
Sdk sdk = settingsStep.getSdk();
final Project project = ProjectManager.getInstance().getDefaultProject();
@@ -104,56 +100,30 @@ public class GenerateProjectCallback implements NullableConsumer<AbstractProject
}
@Nullable
private static Project generateProject(@NotNull final Project project, @NotNull final AbstractProjectSettingsStep settings) {
private static Project generateProject(@NotNull final Project project,
@NotNull final ProjectSettingsStepBase 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;
return NewDirectoryProjectAction.doGenerateProject(project, settings.getProjectLocation(), generator,
new Function<VirtualFile, Object>() {
@Override
public Object fun(VirtualFile file) {
return computeProjectSettings(generator, (ProjectSpecificSettingsStep)settings);
}
});
}
public static Object computeProjectSettings(DirectoryProjectGenerator generator, ProjectSpecificSettingsStep settings) {
Object projectSettings = null;
if (generator instanceof PythonProjectGenerator) {
projectSettings = ((PythonProjectGenerator)generator).getProjectSettings();
}
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);
}
else if (generator instanceof WebProjectTemplate) {
projectSettings = ((WebProjectTemplate)generator).getPeer().getSettings();
}
String generatorName = generator == null ? "empty" : ConvertUsagesUtil.ensureProperKey(generator.getName());
UsageTrigger.trigger("NewDirectoryProjectAction." + generatorName);
RecentProjectsManager.getInstance().setLastProjectCreationLocation(location.getParent());
return PlatformProjectOpenProcessor.doOpenProject(baseDir, null, false, -1, new ProjectOpenedCallback() {
@Override
public void projectOpened(Project project, Module module) {
if (generator != null) {
Object projectSettings = null;
if (generator instanceof PythonProjectGenerator) {
projectSettings = ((PythonProjectGenerator)generator).getProjectSettings();
}
else if (generator instanceof WebProjectTemplate) {
projectSettings = ((WebProjectTemplate)generator).getPeer().getSettings();
}
if (projectSettings instanceof PyNewProjectSettings) {
((PyNewProjectSettings)projectSettings).setSdk(settings.getSdk());
((PyNewProjectSettings)projectSettings).setInstallFramework(settings.installFramework());
}
//noinspection unchecked
generator.generateProject(project, baseDir, projectSettings, module);
}
}
}, false);
if (projectSettings instanceof PyNewProjectSettings) {
((PyNewProjectSettings)projectSettings).setSdk(settings.getSdk());
((PyNewProjectSettings)projectSettings).setInstallFramework(settings.installFramework());
}
return projectSettings;
}
}
@@ -16,6 +16,8 @@
package com.jetbrains.python.newProject.actions;
import com.intellij.icons.AllIcons;
import com.intellij.ide.util.projectWizard.ProjectSettingsStepBase;
import com.intellij.ide.util.projectWizard.actions.ProjectSpecificAction;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.project.DumbAware;
import com.intellij.platform.DirectoryProjectGenerator;
@@ -26,12 +28,12 @@ import java.util.List;
public class PluginSpecificProjectsStep extends DefaultActionGroup implements DumbAware {
public PluginSpecificProjectsStep(@NotNull final NullableConsumer<AbstractProjectSettingsStep> callback,
public PluginSpecificProjectsStep(@NotNull final NullableConsumer<ProjectSettingsStepBase> callback,
@NotNull final List<DirectoryProjectGenerator> projectGenerators, boolean isWelcomeScreen) {
super("Plugin-specific", true);
getTemplatePresentation().setIcon(AllIcons.Nodes.PluginLogo);
for (DirectoryProjectGenerator generator : projectGenerators) {
ProjectSpecificAction action = new ProjectSpecificAction(callback, generator, isWelcomeScreen);
ProjectSpecificAction action = new ProjectSpecificAction(generator, new ProjectSpecificSettingsStep(generator, callback));
if (isWelcomeScreen) {
addAll(action.getChildren(null));
} else {
@@ -1,52 +0,0 @@
/*
* Copyright 2000-2014 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, boolean isWelcomeScreen) {
this(callback, projectGenerator, projectGenerator.getName(), isWelcomeScreen);
}
public ProjectSpecificAction(@NotNull final NullableConsumer<AbstractProjectSettingsStep> callback,
@NotNull final DirectoryProjectGenerator projectGenerator,
@NotNull final String name, boolean isWelcomeScreen) {
super(name, true);
getTemplatePresentation().setIcon(projectGenerator.getLogo());
mySettings = new ProjectSpecificSettingsStep(projectGenerator, callback, isWelcomeScreen);
add(mySettings);
}
public Sdk getSdk() {
return mySettings.getSdk();
}
@Override
public void actionPerformed(AnActionEvent e) {
super.actionPerformed(e);
}
}
@@ -15,24 +15,56 @@
*/
package com.jetbrains.python.newProject.actions;
import com.intellij.execution.ExecutionException;
import com.intellij.facet.ui.ValidationResult;
import com.intellij.ide.util.projectWizard.ProjectSettingsStepBase;
import com.intellij.ide.util.projectWizard.WebProjectTemplate;
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.VerticalFlowLayout;
import com.intellij.openapi.util.Condition;
import com.intellij.platform.DirectoryProjectGenerator;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.HideableDecorator;
import com.intellij.util.NullableConsumer;
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.newProject.PythonProjectGenerator;
import com.jetbrains.python.packaging.PyPackageManager;
import com.jetbrains.python.sdk.PySdkUtil;
import com.jetbrains.python.sdk.PythonSdkType;
import icons.PythonIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
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;
public class ProjectSpecificSettingsStep extends AbstractProjectSettingsStep implements DumbAware {
public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase implements DumbAware {
private PythonSdkChooserCombo mySdkCombo;
private boolean myInstallFramework;
private Sdk mySdk;
public ProjectSpecificSettingsStep(@NotNull final DirectoryProjectGenerator projectGenerator,
@NotNull final NullableConsumer<AbstractProjectSettingsStep> callback, boolean isWelcomeScreen) {
super(projectGenerator, callback, isWelcomeScreen);
@NotNull final NullableConsumer<ProjectSettingsStepBase> callback) {
super(projectGenerator, callback);
}
private static boolean acceptsRemoteSdk(DirectoryProjectGenerator generator) {
if (generator instanceof PyFrameworkProjectGenerator) {
return ((PyFrameworkProjectGenerator)generator).acceptsRemoteSdk();
}
return true;
}
@Override
@@ -58,4 +90,188 @@ public class ProjectSpecificSettingsStep extends AbstractProjectSettingsStep imp
}
return null;
}
public Sdk getSdk() {
if (mySdk != null) return mySdk;
return (Sdk)mySdkCombo.getComboBox().getSelectedItem();
}
public void setSdk(final Sdk sdk) {
mySdk = sdk;
}
public boolean installFramework() {
return myInstallFramework;
}
@Override
protected void registerValidators() {
super.registerValidators();
mySdkCombo.getComboBox().addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
checkValid();
}
});
final ActionListener listener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
checkValid();
}
};
mySdkCombo.getComboBox().addActionListener(listener);
mySdkCombo.addActionListener(listener);
}
@Nullable
@Override
protected JPanel extendBasePanel() {
if (myProjectGenerator instanceof PythonProjectGenerator)
return ((PythonProjectGenerator)myProjectGenerator).extendBasePanel();
return super.extendBasePanel();
}
@Override
public boolean checkValid() {
myInstallFramework = false;
if (!super.checkValid()) {
return false;
}
final Sdk sdk = getSdk();
if (myProjectGenerator instanceof PythonProjectGenerator) {
final ValidationResult warningResult = ((PythonProjectGenerator)myProjectGenerator).warningValidation(sdk);
if (!warningResult.isOk()) {
setWarningText(warningResult.getErrorMessage());
}
}
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)) {
String warningText = frameworkName + " will be installed on selected interpreter";
myInstallFramework = true;
final PyPackageManager packageManager = PyPackageManager.getInstance(sdk);
boolean hasManagement = false;
try {
hasManagement = packageManager.hasManagement(PySdkUtil.isRemote(sdk));
}
catch (ExecutionException ignored) {
}
if (!hasManagement) {
warningText = "Python packaging tools and " + warningText;
}
setWarningText(warningText);
}
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;
}
private boolean isFrameworkInstalled(Sdk sdk) {
PyFrameworkProjectGenerator projectGenerator = (PyFrameworkProjectGenerator)getProjectGenerator();
return projectGenerator != null && projectGenerator.isFrameworkInstalled(sdk);
}
@Override
protected JPanel createBasePanel() {
final JPanel panel = super.createBasePanel();
final GridBagConstraints c = new GridBagConstraints();
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 java.util.List<Sdk> sdks = PyConfigurableInterpreterList.getInstance(project).getAllPythonSdks();
VirtualEnvProjectFilter.removeAllAssociated(sdks);
Sdk compatibleSdk = sdks.isEmpty() ? null : sdks.iterator().next();
DirectoryProjectGenerator generator = getProjectGenerator();
if (generator instanceof PyFrameworkProjectGenerator && !((PyFrameworkProjectGenerator)generator).supportsPython3()) {
if (compatibleSdk != null && PythonSdkType.getLanguageLevelForSdk(compatibleSdk).isPy3K()) {
Sdk python2Sdk = PythonSdkType.findPython2Sdk(sdks);
if (python2Sdk != null) {
compatibleSdk = python2Sdk;
}
}
}
final Sdk preferred = compatibleSdk;
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);
final JPanel basePanelExtension = extendBasePanel();
if (basePanelExtension != null) {
c.gridwidth = 2;
c.gridy = 2;
c.gridx = 0;
panel.add(basePanelExtension, c);
}
return panel;
}
@Override
protected void initGeneratorListeners() {
super.initGeneratorListeners();
if (myProjectGenerator instanceof PythonProjectGenerator) {
((PythonProjectGenerator)myProjectGenerator).addSettingsStateListener(new PythonProjectGenerator.SettingsListener() {
@Override
public void stateChanged() {
checkValid();
}
});
}
myLocationField.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
if (myProjectGenerator instanceof PythonProjectGenerator) {
String path = myLocationField.getText().trim();
if (path.endsWith(File.separator)) {
path = path.substring(0, path.length() - File.separator.length());
}
int ind = path.lastIndexOf(File.separator);
if (ind != -1) {
String projectName = path.substring(ind + 1, path.length());
((PythonProjectGenerator)myProjectGenerator).locationChanged(projectName);
}
}
}
});
}
}
@@ -16,6 +16,8 @@
package com.jetbrains.python.newProject.actions;
import com.google.common.collect.Lists;
import com.intellij.ide.util.projectWizard.ProjectSettingsStepBase;
import com.intellij.ide.util.projectWizard.actions.ProjectSpecificAction;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.DumbAware;
@@ -40,9 +42,10 @@ public class PyCharmNewProjectStep extends DefaultActionGroup implements DumbAwa
public PyCharmNewProjectStep(@NotNull final String name, @Nullable final Runnable runnable, boolean isWelcomeScreen) {
super(name, true);
final NullableConsumer<AbstractProjectSettingsStep> callback = new GenerateProjectCallback(runnable);
final NullableConsumer<ProjectSettingsStepBase> callback = new GenerateProjectCallback(runnable);
final ProjectSpecificAction action = new ProjectSpecificAction(callback, new PythonBaseProjectGenerator(), isWelcomeScreen);
final PythonBaseProjectGenerator baseGenerator = new PythonBaseProjectGenerator();
final ProjectSpecificAction action = new ProjectSpecificAction(baseGenerator, new ProjectSpecificSettingsStep(baseGenerator, callback));
if (isWelcomeScreen) {
addAll(action.getChildren(null));
} else {
@@ -65,7 +68,7 @@ public class PyCharmNewProjectStep extends DefaultActionGroup implements DumbAwa
List<DirectoryProjectGenerator> pluginSpecificGenerators = Lists.newArrayList();
for (DirectoryProjectGenerator generator : generators) {
if (generator instanceof PythonProjectGenerator) {
ProjectSpecificAction group = new ProjectSpecificAction(callback, generator, isWelcomeScreen);
ProjectSpecificAction group = new ProjectSpecificAction(generator, new ProjectSpecificSettingsStep(generator, callback));
if (isWelcomeScreen) {
addAll(group.getChildren(null));
} else {