diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.form b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.form deleted file mode 100644 index c13bc229b973..000000000000 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.form +++ /dev/null @@ -1,103 +0,0 @@ - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java index 352a9613d9a1..2ef3e8d152f5 100644 --- a/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java +++ b/python/educational-core/student/src/com/jetbrains/edu/learning/ui/StudyNewProjectPanel.java @@ -4,7 +4,6 @@ import com.intellij.facet.ui.FacetValidatorsManager; import com.intellij.facet.ui.ValidationResult; import com.intellij.icons.AllIcons; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; import com.intellij.openapi.progress.ProgressManager; @@ -12,6 +11,7 @@ import com.intellij.openapi.project.DefaultProjectFactory; import com.intellij.openapi.project.DefaultProjectFactoryImpl; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.ui.*; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.openapi.ui.popup.PopupStep; @@ -19,15 +19,17 @@ import com.intellij.openapi.ui.popup.util.BaseListPopupStep; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.AncestorListenerAdapter; +import com.intellij.ui.JBColor; +import com.intellij.ui.PanelWithAnchor; import com.jetbrains.edu.learning.StudyUtils; import com.jetbrains.edu.learning.courseFormat.Course; import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import com.jetbrains.edu.learning.stepic.CourseInfo; import com.jetbrains.edu.learning.stepic.EduStepicConnector; -import com.jetbrains.edu.learning.stepic.LoginDialog; import com.jetbrains.edu.learning.stepic.StepicUser; import icons.InteractiveLearningIcons; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.AncestorEvent; @@ -41,39 +43,38 @@ import java.util.List; * author: liana * data: 7/31/14. */ -public class StudyNewProjectPanel { - private static final Logger LOG = Logger.getInstance(StudyNewProjectPanel.class); - private final String LOGIN_TO_STEPIC = "Login to Stepic"; +public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor { private List myAvailableCourses = new ArrayList(); private JButton myBrowseButton; - private JComboBox myCoursesComboBox; + private ComboBox myCoursesComboBox; private JButton myRefreshButton; - private JPanel myContentPanel; private JLabel myAuthorLabel; - private JLabel myLabel; private JPanel myInfoPanel; - private JTextPane myDescriptionLabel; + private JTextPane myDescriptionPane; + private JComponent myAnchor; private final StudyProjectGenerator myGenerator; private static final String CONNECTION_ERROR = "Failed to download courses.
Check your Internet connection."; private static final String INVALID_COURSE = "Selected course is invalid"; private FacetValidatorsManager myValidationManager; private boolean isComboboxInitialized; - private String LOGIN_TO_STEPIC_MESSAGE = "Login to Stepic to open the adaptive course ";; + private static final String LOGIN_TO_STEPIC_MESSAGE = "Login to Stepic to open the adaptive course "; + private static final String LOGIN_TO_STEPIC = "Login to Stepic"; public StudyNewProjectPanel(@NotNull final StudyProjectGenerator generator) { + super(new VerticalFlowLayout()); + layoutPanel(); + + myAnchor = myCoursesComboBox; myGenerator = generator; - myBrowseButton.setPreferredSize(new Dimension(28, 28)); - myRefreshButton.setPreferredSize(new Dimension(28, 28)); initListeners(); myRefreshButton.setVisible(true); - myRefreshButton.putClientProperty("JButton.buttonType", "null"); + myRefreshButton.setIcon(AllIcons.Actions.Refresh); - myLabel.setPreferredSize(new JLabel("Project name").getPreferredSize()); - myContentPanel.addAncestorListener(new AncestorListenerAdapter() { + addAncestorListener(new AncestorListenerAdapter() { @Override public void ancestorMoved(AncestorEvent event) { - if (!isComboboxInitialized && myContentPanel.isVisible()) { + if (!isComboboxInitialized && isVisible()) { isComboboxInitialized = true; initCoursesCombobox(); } @@ -81,6 +82,34 @@ public class StudyNewProjectPanel { }); } + private void layoutPanel() { + myCoursesComboBox = new ComboBox(); + final LabeledComponent coursesCombo = LabeledComponent.create(myCoursesComboBox, "Courses:", BorderLayout.WEST); + add(coursesCombo); + + myRefreshButton = new FixedSizeButton(coursesCombo); + myBrowseButton = new FixedSizeButton(coursesCombo); + + final JPanel panel = new JPanel(new BorderLayout()); + final JLabel invisibleLabel = new JLabel(); + invisibleLabel.setPreferredSize(new JLabel("Location: ").getPreferredSize()); + panel.add(invisibleLabel, BorderLayout.WEST); + + myInfoPanel = new JPanel(new VerticalFlowLayout()); + myAuthorLabel = new JLabel(); + myDescriptionPane = new JTextPane(); + myDescriptionPane.setEditable(true); + myDescriptionPane.setEnabled(true); + myAuthorLabel.setEnabled(true); + myDescriptionPane.setPreferredSize(new Dimension(150, 100)); + myInfoPanel.add(myAuthorLabel); + myInfoPanel.add(myDescriptionPane); + myInfoPanel.setBorder(BorderFactory.createLineBorder(new JBColor(10067616, 10067616))); + + panel.add(myInfoPanel, BorderLayout.CENTER); + add(panel); + } + private void initCoursesCombobox() { myAvailableCourses = myGenerator.getCoursesUnderProgress(false, "Getting Available Courses", ProjectManager.getInstance().getDefaultProject()); @@ -92,11 +121,11 @@ public class StudyNewProjectPanel { final CourseInfo selectedCourse = StudyUtils.getFirst(myAvailableCourses); final String authorsString = Course.getAuthorsString(selectedCourse.getAuthors()); myAuthorLabel.setText(!StringUtil.isEmptyOrSpaces(authorsString) ? "Author: " + authorsString : ""); - myDescriptionLabel.setText(selectedCourse.getDescription()); - myDescriptionLabel.setEditable(false); + myDescriptionPane.setText(selectedCourse.getDescription()); + myDescriptionPane.setEditable(false); //setting the first course in list as selected myGenerator.setSelectedCourse(selectedCourse); - + if (selectedCourse.isAdaptive() && !myGenerator.isLoggedIn()) { setError(LOGIN_TO_STEPIC_MESSAGE); } @@ -107,7 +136,6 @@ public class StudyNewProjectPanel { } private void setupBrowseButton() { - myBrowseButton.putClientProperty("JButton.buttonType", "null"); myBrowseButton.setIcon(InteractiveLearningIcons.InterpreterGear); final FileChooserDescriptor fileChooser = new FileChooserDescriptor(true, false, false, true, false, false) { @Override @@ -188,15 +216,19 @@ public class StudyNewProjectPanel { myValidationManager.validate(); } } - - public JPanel getContentPanel() { - return myContentPanel; - } - public void registerValidators(final FacetValidatorsManager manager) { myValidationManager = manager; } + @Override + public JComponent getAnchor() { + return myAnchor; + } + + @Override + public void setAnchor(@Nullable JComponent anchor) { + myAnchor = anchor; + } /** * Handles refreshing courses @@ -248,14 +280,14 @@ public class StudyNewProjectPanel { CourseInfo selectedCourse = (CourseInfo)cb.getSelectedItem(); if (selectedCourse == null || selectedCourse.equals(CourseInfo.INVALID_COURSE)) { myAuthorLabel.setText(""); - myDescriptionLabel.setText(""); + myDescriptionPane.setText(""); setError(INVALID_COURSE); return; } final String authorsString = Course.getAuthorsString(selectedCourse.getAuthors()); myAuthorLabel.setText(!StringUtil.isEmptyOrSpaces(authorsString) ? "Author: " + authorsString : ""); myCoursesComboBox.removeItem(CourseInfo.INVALID_COURSE); - myDescriptionLabel.setText(selectedCourse.getDescription()); + myDescriptionPane.setText(selectedCourse.getDescription()); myGenerator.setSelectedCourse(selectedCourse); setOK(); @@ -275,25 +307,49 @@ public class StudyNewProjectPanel { return myInfoPanel; } - private class AddRemoteDialog extends LoginDialog { + private class AddRemoteDialog extends DialogWrapper { + + private final StudyAddRemoteCourse myRemoteCourse; protected AddRemoteDialog() { - super(); + super(null); + setTitle("Login To Stepic"); + myRemoteCourse = new StudyAddRemoteCourse(); + init(); + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + return myRemoteCourse.getContentPanel(); + } + + @Nullable + @Override + public JComponent getPreferredFocusedComponent() { + return myRemoteCourse.getLoginField(); } @Override protected void doOKAction() { - if (!validateLoginAndPasswordFields()) return; - super.doJustOkAction(); + super.doOKAction(); + if (StringUtil.isEmptyOrSpaces(myRemoteCourse.getLogin())) { + myRemoteCourse.setError("Please, enter your login"); + return; + } + if (StringUtil.isEmptyOrSpaces(myRemoteCourse.getPassword())) { + myRemoteCourse.setError("Please, enter your password"); + return; + } ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); - final StepicUser stepicUser = StudyUtils.execCancelable(() -> EduStepicConnector.login(myLoginPanel.getLogin(), - myLoginPanel.getPassword())); + final StepicUser stepicUser = StudyUtils.execCancelable(() -> EduStepicConnector.login(myRemoteCourse.getLogin(), + myRemoteCourse.getPassword())); if (stepicUser != null) { - stepicUser.setEmail(myLoginPanel.getLogin()); - stepicUser.setPassword(myLoginPanel.getPassword()); + stepicUser.setEmail(myRemoteCourse.getLogin()); + stepicUser.setPassword(myRemoteCourse.getPassword()); myGenerator.myUser = stepicUser; myGenerator.setEnrolledCoursesIds(EduStepicConnector.getEnrolledCoursesIds()); @@ -306,6 +362,6 @@ public class StudyNewProjectPanel { setError("Failed to login"); } }, "Signing In And Getting Stepic Course List", true, new DefaultProjectFactoryImpl().getDefaultProject()); - } + } } } diff --git a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java index be783451faab..5c302e4247f5 100644 --- a/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java +++ b/python/educational-python/student-python/src/com/jetbrains/edu/learning/PyStudyDirectoryProjectGenerator.java @@ -14,7 +14,6 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.util.ThrowableComputable; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.platform.DirectoryProjectGenerator; import com.intellij.psi.PsiDirectory; @@ -24,6 +23,7 @@ import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator; import com.jetbrains.edu.learning.stepic.CourseInfo; import com.jetbrains.edu.learning.stepic.EduStepicConnector; import com.jetbrains.edu.learning.ui.StudyNewProjectPanel; +import com.jetbrains.python.configuration.PyConfigurableInterpreterList; import com.jetbrains.python.newProject.PythonProjectGenerator; import icons.InteractiveLearningPythonIcons; import org.jetbrains.annotations.Nls; @@ -40,6 +40,7 @@ import java.util.List; public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator implements DirectoryProjectGenerator { private static final Logger LOG = Logger.getInstance(PyStudyDirectoryProjectGenerator.class.getName()); private final StudyProjectGenerator myGenerator; + private static final String NO_PYTHON_INTERPRETER = "Add python interpreter."; public ValidationResult myValidationResult = new ValidationResult("selected course is not valid"); private StudyNewProjectPanel mySettingsPanel; @@ -115,10 +116,16 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator imp LOG.error("Can't copy test_helper.py " + exception.getMessage()); } } - + @NotNull @Override public ValidationResult validate(@NotNull String s) { + final Project project = ProjectManager.getInstance().getDefaultProject(); + final List sdks = PyConfigurableInterpreterList.getInstance(project).getAllPythonSdks(); + if (sdks.isEmpty()) { + myValidationResult = new ValidationResult(NO_PYTHON_INTERPRETER); + } + return myValidationResult; } @@ -129,7 +136,7 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator imp @Nullable @Override public JPanel extendBasePanel() throws ProcessCanceledException { - return mySettingsPanel.getContentPanel(); + return mySettingsPanel; } public List getCourses() { @@ -144,26 +151,25 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator imp return myGenerator; } + @Override + public boolean hideInterpreter() { + return true; + } + @Nullable @Override - public BooleanFunction beforeProjectGenerated(@NotNull Sdk sdk) { - return new BooleanFunction() { - @Override - public boolean fun(PythonProjectGenerator generator) { - final List enrolledCoursesIds = myGenerator.getEnrolledCoursesIds(); - final CourseInfo course = (CourseInfo)mySettingsPanel.getCoursesComboBox().getSelectedItem(); - if (course.isAdaptive() && !enrolledCoursesIds.contains(course.getId())) { - ProgressManager.getInstance().runProcessWithProgressSynchronously(new ThrowableComputable() { - @Override - public Boolean compute() throws RuntimeException { - ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); - return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(course.getId())); - } - }, "Creating Course", true, ProjectManager.getInstance().getDefaultProject()); - - } - return true; + public BooleanFunction beforeProjectGenerated(@Nullable Sdk sdk) { + return generator -> { + final List enrolledCoursesIds = myGenerator.getEnrolledCoursesIds(); + final CourseInfo course = (CourseInfo)mySettingsPanel.getCoursesComboBox().getSelectedItem(); + if (course.isAdaptive() && !enrolledCoursesIds.contains(course.getId())) { + ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> { + ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true); + return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(course.getId())); + }, "Creating Course", true, ProjectManager.getInstance().getDefaultProject()); + } + return true; }; } } diff --git a/python/educational-python/student-python/src/com/jetbrains/edu/learning/actions/PyStudyIntroductionCourseAction.java b/python/educational-python/student-python/src/com/jetbrains/edu/learning/actions/PyStudyIntroductionCourseAction.java index eb936e082c51..de28c8314560 100644 --- a/python/educational-python/student-python/src/com/jetbrains/edu/learning/actions/PyStudyIntroductionCourseAction.java +++ b/python/educational-python/student-python/src/com/jetbrains/edu/learning/actions/PyStudyIntroductionCourseAction.java @@ -19,12 +19,8 @@ import com.intellij.ide.impl.ProjectUtil; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Presentation; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectManager; -import com.intellij.openapi.projectRoots.Sdk; import com.jetbrains.edu.learning.PyStudyDirectoryProjectGenerator; import com.jetbrains.edu.learning.stepic.CourseInfo; -import com.jetbrains.python.configuration.PyConfigurableInterpreterList; import com.jetbrains.python.newProject.actions.GenerateProjectCallback; import com.jetbrains.python.newProject.actions.ProjectSpecificSettingsStep; import icons.InteractiveLearningPythonIcons; @@ -76,10 +72,6 @@ public class PyStudyIntroductionCourseAction extends AnAction { step.setLocation(projectDir.toString()); generator.setSelectedCourse(introCourse); - final Project project = ProjectManager.getInstance().getDefaultProject(); - final List sdks = PyConfigurableInterpreterList.getInstance(project).getAllPythonSdks(); - Sdk sdk = sdks.isEmpty() ? null : sdks.iterator().next(); - step.setSdk(sdk); callback.consume(step); } } diff --git a/python/ide/src/com/jetbrains/python/newProject/actions/GenerateProjectCallback.java b/python/ide/src/com/jetbrains/python/newProject/actions/GenerateProjectCallback.java index 51285a044a78..fdac01bed22d 100644 --- a/python/ide/src/com/jetbrains/python/newProject/actions/GenerateProjectCallback.java +++ b/python/ide/src/com/jetbrains/python/newProject/actions/GenerateProjectCallback.java @@ -15,6 +15,7 @@ */ package com.jetbrains.python.newProject.actions; +import com.intellij.execution.ExecutionException; import com.intellij.ide.util.projectWizard.AbstractNewProjectStep; import com.intellij.ide.util.projectWizard.ProjectSettingsStepBase; import com.intellij.ide.util.projectWizard.WebProjectTemplate; @@ -36,10 +37,13 @@ import com.intellij.util.NullableConsumer; import com.jetbrains.python.configuration.PyConfigurableInterpreterList; import com.jetbrains.python.newProject.PyNewProjectSettings; import com.jetbrains.python.newProject.PythonProjectGenerator; +import com.jetbrains.python.packaging.PyPackageManager; import com.jetbrains.python.sdk.*; +import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; import java.util.List; public class GenerateProjectCallback implements NullableConsumer { @@ -50,34 +54,17 @@ public class GenerateProjectCallback implements NullableConsumer() { - @Override - public VirtualFile compute() { - return LocalFileSystem.getInstance().refreshAndFindFileByPath(name); - } - }); - PySdkService.getInstance().solidifySdk(sdk); - sdk = SdkConfigurationUtil.createAndAddSDK(sdkHome.getPath(), PythonSdkType.getInstance()); - if (sdk != null) { - PythonSdkUpdater.updateOrShowError(sdk, null, project, null); - } - model.addSdk(sdk); - settingsStep.setSdk(sdk); - try { - model.apply(); - } - catch (ConfigurationException exception) { - LOG.error("Error adding detected python interpreter " + exception.getMessage()); - } + if (sdk instanceof PyDetectedSdk) { + addDetectedSdk(settingsStep, sdk); } - Project newProject = generateProject(project, settingsStep); + else if (sdk == null) { + createAndAddVirtualEnv(settingsStep); + } + sdk = settingsStep.getSdk(); + + final Project newProject = generateProject(settingsStep); if (newProject != null) { SdkConfigurationUtil.setDirectoryProjectSdk(newProject, sdk); final List sdks = PythonSdkType.getAllSdks(); @@ -90,12 +77,72 @@ public class GenerateProjectCallback implements NullableConsumer() { + @Override + public VirtualFile compute() { + return LocalFileSystem.getInstance().refreshAndFindFileByPath(name); + } + }); + PySdkService.getInstance().solidifySdk(sdk); + sdk = SdkConfigurationUtil.createAndAddSDK(sdkHome.getPath(), PythonSdkType.getInstance()); + if (sdk != null) { + PythonSdkUpdater.updateOrShowError(sdk, null, project, null); + } + + model.addSdk(sdk); + settingsStep.setSdk(sdk); + try { + model.apply(); + } + catch (ConfigurationException exception) { + LOG.error("Error adding detected python interpreter " + exception.getMessage()); + } + } + + private static void createAndAddVirtualEnv(final ProjectSpecificSettingsStep settingsStep) { + final Project project = ProjectManager.getInstance().getDefaultProject(); + final ProjectSdksModel model = PyConfigurableInterpreterList.getInstance(project).getModel(); + final List flavors = PythonSdkFlavor.getApplicableFlavors(false); + String baseSdk = null; + for (PythonSdkFlavor flavor : flavors) { + final Collection baseSdks = flavor.suggestHomePaths(); + if (!baseSdks.isEmpty()) { + baseSdk = baseSdks.iterator().next(); + } + } + if (baseSdk != null) { + final PyPackageManager packageManager = PyPackageManager.getInstance(new PyDetectedSdk(baseSdk)); + try { + final String path = packageManager.createVirtualEnv(settingsStep.getProjectLocation() + "/.idea/VirtualEnvironment", false); + AbstractCreateVirtualEnvDialog.setupVirtualEnvSdk(path, true, new AbstractCreateVirtualEnvDialog.VirtualEnvCallback() { + @Override + public void virtualEnvCreated(Sdk createdSdk, boolean associateWithProject) { + settingsStep.setSdk(createdSdk); + model.addSdk(createdSdk); + try { + model.apply(); + } + catch (ConfigurationException exception) { + LOG.error("Error adding created virtual env " + exception.getMessage()); + } + } + }); + } + catch (ExecutionException e) { + LOG.warn("Failed to create virtual env " + e.getMessage()); + } + } + } + @Nullable - private static Project generateProject(@NotNull final Project project, - @NotNull final ProjectSettingsStepBase settings) { + private static Project generateProject(@NotNull final ProjectSettingsStepBase settings) { final DirectoryProjectGenerator generator = settings.getProjectGenerator(); final String location = FileUtil.expandUserHome(settings.getProjectLocation()); - return AbstractNewProjectStep.doGenerateProject(project, location, generator, + return AbstractNewProjectStep.doGenerateProject(ProjectManager.getInstance().getDefaultProject(), location, generator, file -> computeProjectSettings(generator, (ProjectSpecificSettingsStep)settings)); } diff --git a/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificSettingsStep.java b/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificSettingsStep.java index 3ff75c66f91a..09a7a0d78210 100644 --- a/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificSettingsStep.java +++ b/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificSettingsStep.java @@ -23,8 +23,7 @@ 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.LabeledComponent; -import com.intellij.openapi.ui.VerticalFlowLayout; +import com.intellij.openapi.ui.*; import com.intellij.openapi.util.text.StringUtil; import com.intellij.platform.DirectoryProjectGenerator; import com.intellij.ui.DocumentAdapter; @@ -103,7 +102,9 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme } public Sdk getSdk() { + if (!(myProjectGenerator instanceof PythonProjectGenerator)) return null; if (mySdk != null) return mySdk; + if (((PythonProjectGenerator)myProjectGenerator).hideInterpreter()) return null; return (Sdk)mySdkCombo.getComboBox().getSelectedItem(); } @@ -118,7 +119,7 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme @Override protected void registerValidators() { super.registerValidators(); - if (myProjectGenerator instanceof PythonProjectGenerator) { + if (myProjectGenerator instanceof PythonProjectGenerator && !((PythonProjectGenerator)myProjectGenerator).hideInterpreter()) { mySdkCombo.getComboBox().addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent event) { @@ -136,13 +137,6 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme } } - @Nullable - protected JPanel extendBasePanel() { - if (myProjectGenerator instanceof PythonProjectGenerator) - return ((PythonProjectGenerator)myProjectGenerator).extendBasePanel(); - return null; - } - @Override public boolean checkValid() { myInstallFramework = false; @@ -150,11 +144,12 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme return false; } - if ((myProjectGenerator instanceof PythonProjectGenerator)) { + if (myProjectGenerator instanceof PythonProjectGenerator) { final Sdk sdk = getSdk(); - if (sdk == null) { - setErrorText("No Python interpreter selected"); + if (!((PythonProjectGenerator)myProjectGenerator).hideInterpreter()) { + setErrorText("No Python interpreter selected"); + } return false; } else if (PythonSdkType.isInvalid(sdk)) { @@ -213,39 +208,81 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme @Override protected JPanel createBasePanel() { - final JPanel panel = super.createBasePanel(); - if (myProjectGenerator instanceof PythonProjectGenerator) { - final Project project = ProjectManager.getInstance().getDefaultProject(); - final List 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 BorderLayout layout = new BorderLayout(); + + final JPanel locationPanel = new JPanel(layout); + + final JPanel panel = new JPanel(new VerticalFlowLayout(0, 2)); + final LabeledComponent location = createLocationComponent(); + location.setLabelLocation(BorderLayout.WEST); + + locationPanel.add(location, BorderLayout.CENTER); + panel.add(locationPanel); + if (((PythonProjectGenerator)myProjectGenerator).hideInterpreter()) { + addInterpreterButton(locationPanel, location); + } + else { + final LabeledComponent labeled = createInterpreterCombo(); + UIUtil.mergeComponentsWithAnchor(labeled, location); + panel.add(labeled); } - final Sdk preferred = compatibleSdk; - mySdkCombo = new PythonSdkChooserCombo(project, sdks, sdk -> sdk == preferred); - mySdkCombo.setButtonIcon(PythonIcons.Python.InterpreterGear); - - final LabeledComponent labeled = LabeledComponent.create(mySdkCombo, "Interpreter"); - labeled.setLabelLocation(BorderLayout.WEST); - UIUtil.mergeComponentsWithAnchor(labeled, (PanelWithAnchor)panel.getComponent(0)); - panel.add(labeled); + final JPanel basePanelExtension = ((PythonProjectGenerator)myProjectGenerator).extendBasePanel(); + if (basePanelExtension != null) { + UIUtil.mergeComponentsWithAnchor((PanelWithAnchor)basePanelExtension, location); + panel.add(basePanelExtension); + } + return panel; } - final JPanel basePanelExtension = extendBasePanel(); - if (basePanelExtension != null) { - panel.add(basePanelExtension); + return super.createBasePanel(); + } + + private void addInterpreterButton(final JPanel locationPanel, final LabeledComponent location) { + final JButton interpreterButton = new FixedSizeButton(location); + interpreterButton.setIcon(PythonIcons.Python.Python); + interpreterButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + final DialogBuilder builder = new DialogBuilder(); + final JPanel panel = new JPanel(); + final LabeledComponent interpreterCombo = createInterpreterCombo(); + if (mySdk != null) { + mySdkCombo.getComboBox().setSelectedItem(mySdk); + } + panel.add(interpreterCombo); + builder.setCenterPanel(panel); + builder.setTitle("Select Python Interpreter"); + if (builder.showAndGet()) { + mySdk = (Sdk)mySdkCombo.getComboBox().getSelectedItem(); + } + } + }); + locationPanel.add(interpreterButton, BorderLayout.EAST); + } + + @NotNull + private LabeledComponent createInterpreterCombo() { + final Project project = ProjectManager.getInstance().getDefaultProject(); + final List 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; + } + } } - return panel; + final Sdk preferred = compatibleSdk; + mySdkCombo = new PythonSdkChooserCombo(project, sdks, sdk -> sdk == preferred); + mySdkCombo.setButtonIcon(PythonIcons.Python.InterpreterGear); + + return LabeledComponent.create(mySdkCombo, "Interpreter", BorderLayout.WEST); } @Override diff --git a/python/openapi/src/com/jetbrains/python/newProject/PythonProjectGenerator.java b/python/openapi/src/com/jetbrains/python/newProject/PythonProjectGenerator.java index 945149868e47..ba2723ad8c32 100644 --- a/python/openapi/src/com/jetbrains/python/newProject/PythonProjectGenerator.java +++ b/python/openapi/src/com/jetbrains/python/newProject/PythonProjectGenerator.java @@ -67,10 +67,14 @@ public abstract class PythonProjectGenerator { } @Nullable - public BooleanFunction beforeProjectGenerated(@NotNull final Sdk sdk) { + public BooleanFunction beforeProjectGenerated(@Nullable final Sdk sdk) { return null; } + public boolean hideInterpreter() { + return false; + } + public void addErrorLabelMouseListener(@NotNull final MouseListener mouseListener) { myErrorLabelMouseListener = mouseListener; } diff --git a/python/src/com/jetbrains/python/sdk/AbstractCreateVirtualEnvDialog.java b/python/src/com/jetbrains/python/sdk/AbstractCreateVirtualEnvDialog.java index da5b3779871e..af2757ee2e07 100644 --- a/python/src/com/jetbrains/python/sdk/AbstractCreateVirtualEnvDialog.java +++ b/python/src/com/jetbrains/python/sdk/AbstractCreateVirtualEnvDialog.java @@ -72,7 +72,7 @@ public abstract class AbstractCreateVirtualEnvDialog extends IdeaDialog { void virtualEnvCreated(Sdk sdk, boolean associateWithProject); } - protected static void setupVirtualEnvSdk(final String path, + public static void setupVirtualEnvSdk(final String path, boolean associateWithProject, VirtualEnvCallback callback) { final VirtualFile sdkHome =