diff --git a/platform/lang-impl/src/com/intellij/ide/util/projectWizard/AbstractNewProjectDialog.java b/platform/lang-impl/src/com/intellij/ide/util/projectWizard/AbstractNewProjectDialog.java new file mode 100644 index 000000000000..99b9d715133e --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/util/projectWizard/AbstractNewProjectDialog.java @@ -0,0 +1,78 @@ +/* + * 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.intellij.ide.util.projectWizard; + +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 org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; + +/** + * @author Dennis.Ushakov + */ +public abstract class AbstractNewProjectDialog extends DialogWrapper { + public AbstractNewProjectDialog() { + super(ProjectManager.getInstance().getDefaultProject()); + setTitle(" "); // hack to make native fileChooser work on Mac. See MacFileChooserDialogImpl.MAIN_THREAD_RUNNABLE + init(); + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + final Runnable runnable = new Runnable() { + @Override + public void run() { + close(OK_EXIT_CODE); + } + }; + final DirectoryProjectGenerator[] generators = Extensions.getExtensions(DirectoryProjectGenerator.EP_NAME); + final DefaultActionGroup root = createRootStep(runnable, generators.length == 0 ? "Create Project" : "Select Project Type"); + + 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); + } + }; + } + + protected abstract DefaultActionGroup createRootStep(Runnable runnable, final String name); + + @Override + protected String getHelpId() { + return null; + } + + @NotNull + protected Action[] createActions() { + return new Action[0]; + } +} diff --git a/platform/lang-impl/src/com/intellij/ide/util/projectWizard/ProjectSettingsStepBase.java b/platform/lang-impl/src/com/intellij/ide/util/projectWizard/ProjectSettingsStepBase.java new file mode 100644 index 000000000000..0eb2648791c5 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/util/projectWizard/ProjectSettingsStepBase.java @@ -0,0 +1,236 @@ +/* + * 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.intellij.ide.util.projectWizard; + +import com.intellij.facet.ui.ValidationResult; +import com.intellij.icons.AllIcons; +import com.intellij.ide.impl.ProjectUtil; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; +import com.intellij.openapi.project.DumbAware; +import com.intellij.openapi.ui.*; +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 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.io.File; + +public class ProjectSettingsStepBase extends AbstractActionWithPanel implements DumbAware { + protected final DirectoryProjectGenerator myProjectGenerator; + private final NullableConsumer myCallback; + protected TextFieldWithBrowseButton myLocationField; + protected final File myProjectDirectory; + private JButton myCreateButton; + private JLabel myErrorLabel; + + public ProjectSettingsStepBase(DirectoryProjectGenerator projectGenerator, + NullableConsumer callback) { + super(); + getTemplatePresentation().setIcon(projectGenerator.getLogo()); + getTemplatePresentation().setText(projectGenerator.getName()); + myProjectGenerator = projectGenerator; + myCallback = callback; + myProjectDirectory = FileUtil.findSequentNonexistentFile(new File(ProjectUtil.getBaseDir()), "untitled", ""); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + } + + @Override + public JPanel createPanel() { + final JPanel basePanel = createBasePanel(); + initGeneratorListeners(); + registerValidators(); + + final JPanel mainPanel = new JPanel(new BorderLayout()); + final JPanel scrollPanel = new JPanel(new BorderLayout()); + + //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(ProjectSettingsStepBase.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; + } + + protected void initGeneratorListeners() { + if (myProjectGenerator instanceof WebProjectTemplate) { + ((WebProjectTemplate)myProjectGenerator).getPeer().addSettingsStateListener(new WebProjectGenerator.SettingsStateListener() { + @Override + public void stateChanged(boolean validSettings) { + checkValid(); + } + }); + } + } + + protected Icon getIcon() { + return myProjectGenerator.getLogo(); + } + + protected 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); + + return panel; + } + + @Nullable + protected JPanel 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(); + } + }; + myLocationField.getTextField().addActionListener(listener); + } + + public boolean checkValid() { + if (myLocationField == null) return true; + final String projectName = myLocationField.getText(); + setErrorText(null); + + 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 WebProjectGenerator.GeneratorPeer peer = ((WebProjectTemplate)myProjectGenerator).getPeer(); + final ValidationInfo validationInfo = peer.validate(); + if (validationInfo != null && !peer.isBackgroundJobRunning()) { + setErrorText(validationInfo.message); + 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()); + } + + @Nullable + protected JPanel createAdvancedSettings() { + if (myProjectGenerator instanceof WebProjectTemplate) { + final JPanel jPanel = new JPanel(new VerticalFlowLayout()); + jPanel.add(((WebProjectTemplate)myProjectGenerator).getPeer().getComponent()); + return jPanel; + } + return null; + } + + public DirectoryProjectGenerator getProjectGenerator() { + return myProjectGenerator; + } + + public String getProjectLocation() { + return myLocationField.getText(); + } + + public void setLocation(@NotNull final String location) { + myLocationField.setText(location); + } +} diff --git a/platform/lang-impl/src/com/intellij/ide/util/projectWizard/actions/ProjectSpecificAction.java b/platform/lang-impl/src/com/intellij/ide/util/projectWizard/actions/ProjectSpecificAction.java new file mode 100644 index 000000000000..92f209ceb6aa --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/util/projectWizard/actions/ProjectSpecificAction.java @@ -0,0 +1,35 @@ +/* + * 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.intellij.ide.util.projectWizard.actions; + +import com.intellij.ide.util.projectWizard.ProjectSettingsStepBase; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.project.DumbAware; +import com.intellij.platform.DirectoryProjectGenerator; +import org.jetbrains.annotations.NotNull; + +public class ProjectSpecificAction extends DefaultActionGroup implements DumbAware { + public ProjectSpecificAction(@NotNull final DirectoryProjectGenerator projectGenerator, final ProjectSettingsStepBase step) { + this(projectGenerator, projectGenerator.getName(), step); + } + + public ProjectSpecificAction(@NotNull final DirectoryProjectGenerator projectGenerator, + @NotNull final String name, final ProjectSettingsStepBase step) { + super(name, true); + getTemplatePresentation().setIcon(projectGenerator.getLogo()); + add(step); + } +} diff --git a/platform/platform-impl/src/com/intellij/platform/NewDirectoryProjectAction.java b/platform/platform-impl/src/com/intellij/platform/NewDirectoryProjectAction.java index fc50287afd54..a1f2496f8335 100644 --- a/platform/platform-impl/src/com/intellij/platform/NewDirectoryProjectAction.java +++ b/platform/platform-impl/src/com/intellij/platform/NewDirectoryProjectAction.java @@ -36,6 +36,7 @@ import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.impl.welcomeScreen.NewWelcomeScreen; import com.intellij.projectImport.ProjectOpenedCallback; +import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -59,7 +60,20 @@ public class NewDirectoryProjectAction extends AnAction implements DumbAware { @Nullable protected Project generateProject(Project project, NewDirectoryProjectDialog dlg) { - final File location = new File(FileUtil.toSystemDependentName(dlg.getNewProjectLocation())); + final DirectoryProjectGenerator generator = dlg.getProjectGenerator(); + return doGenerateProject(project, dlg.getNewProjectLocation(), generator, new Function() { + @Override + public Object fun(VirtualFile file) { + return showSettings(generator, file); + } + }); + } + + public static Project doGenerateProject(@Nullable final Project project, + @NotNull final String locationString, + @Nullable final DirectoryProjectGenerator generator, + @NotNull final Function settingsComputable) { + final File location = new File(FileUtil.toSystemDependentName(locationString)); if (!location.exists() && !location.mkdirs()) { String message = ActionsBundle.message("action.NewDirectoryProject.cannot.create.dir", location.getAbsolutePath()); Messages.showErrorDialog(project, message, ActionsBundle.message("action.NewDirectoryProject.title")); @@ -85,15 +99,13 @@ public class NewDirectoryProjectAction extends AnAction implements DumbAware { } } - @SuppressWarnings("unchecked") final DirectoryProjectGenerator generator = dlg.getProjectGenerator(); - String generatorName = generator == null ? "empty" : ConvertUsagesUtil.ensureProperKey(generator.getName()); UsageTrigger.trigger("NewDirectoryProjectAction." + generatorName); Object settings = null; if (generator != null) { try { - settings = showSettings(generator, baseDir); + settings = settingsComputable.fun(baseDir); } catch (ProcessCanceledException e) { return null; diff --git a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyIntroductionCourseAction.java b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyIntroductionCourseAction.java index ed1fff22878f..7f759fa20868 100644 --- a/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyIntroductionCourseAction.java +++ b/python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyIntroductionCourseAction.java @@ -60,7 +60,7 @@ public class StudyIntroductionCourseAction extends AnAction { introCourse = StudyUtils.getFirst(courses.keySet()); } generator.setSelectedCourse(introCourse); - final ProjectSpecificSettingsStep step = new ProjectSpecificSettingsStep(generator, callback, true); + final ProjectSpecificSettingsStep step = new ProjectSpecificSettingsStep(generator, callback); step.createPanel(); // initialize panel to set location step.setLocation(projectDir.toString()); diff --git a/python/ide/src/com/jetbrains/python/newProject/PyCharmNewProjectDialog.java b/python/ide/src/com/jetbrains/python/newProject/PyCharmNewProjectDialog.java index e60cdd021cc0..0450b3deceb2 100644 --- a/python/ide/src/com/jetbrains/python/newProject/PyCharmNewProjectDialog.java +++ b/python/ide/src/com/jetbrains/python/newProject/PyCharmNewProjectDialog.java @@ -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); } } diff --git a/python/ide/src/com/jetbrains/python/newProject/actions/AbstractProjectSettingsStep.java b/python/ide/src/com/jetbrains/python/newProject/actions/AbstractProjectSettingsStep.java deleted file mode 100644 index 5cf46f300b57..000000000000 --- a/python/ide/src/com/jetbrains/python/newProject/actions/AbstractProjectSettingsStep.java +++ /dev/null @@ -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 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 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 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() { - @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; - } - -} 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 a171aacb0249..01c646a71f62 100644 --- a/python/ide/src/com/jetbrains/python/newProject/actions/GenerateProjectCallback.java +++ b/python/ide/src/com/jetbrains/python/newProject/actions/GenerateProjectCallback.java @@ -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 { +public class GenerateProjectCallback implements NullableConsumer { 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() { + @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() { - 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; } } diff --git a/python/ide/src/com/jetbrains/python/newProject/actions/PluginSpecificProjectsStep.java b/python/ide/src/com/jetbrains/python/newProject/actions/PluginSpecificProjectsStep.java index c80827f7d855..3125af11bab3 100644 --- a/python/ide/src/com/jetbrains/python/newProject/actions/PluginSpecificProjectsStep.java +++ b/python/ide/src/com/jetbrains/python/newProject/actions/PluginSpecificProjectsStep.java @@ -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 callback, + public PluginSpecificProjectsStep(@NotNull final NullableConsumer callback, @NotNull final List 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 { diff --git a/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificAction.java b/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificAction.java deleted file mode 100644 index 87fb9127b23a..000000000000 --- a/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificAction.java +++ /dev/null @@ -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 callback, - @NotNull final DirectoryProjectGenerator projectGenerator, boolean isWelcomeScreen) { - this(callback, projectGenerator, projectGenerator.getName(), isWelcomeScreen); - } - - public ProjectSpecificAction(@NotNull final NullableConsumer 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); - } -} 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 180a9f26d22e..475df0f234d4 100644 --- a/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificSettingsStep.java +++ b/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificSettingsStep.java @@ -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 callback, boolean isWelcomeScreen) { - super(projectGenerator, callback, isWelcomeScreen); + @NotNull final NullableConsumer 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 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() { + @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); + } + } + } + }); + } } diff --git a/python/ide/src/com/jetbrains/python/newProject/actions/PyCharmNewProjectStep.java b/python/ide/src/com/jetbrains/python/newProject/actions/PyCharmNewProjectStep.java index cd8d1a986190..361772388469 100644 --- a/python/ide/src/com/jetbrains/python/newProject/actions/PyCharmNewProjectStep.java +++ b/python/ide/src/com/jetbrains/python/newProject/actions/PyCharmNewProjectStep.java @@ -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 callback = new GenerateProjectCallback(runnable); + final NullableConsumer 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 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 {