diff --git a/platform/lang-impl/src/com/intellij/openapi/projectRoots/impl/SdkConfigurationUtil.java b/platform/lang-impl/src/com/intellij/openapi/projectRoots/impl/SdkConfigurationUtil.java index bd3be406ef00..0ff165617df8 100644 --- a/platform/lang-impl/src/com/intellij/openapi/projectRoots/impl/SdkConfigurationUtil.java +++ b/platform/lang-impl/src/com/intellij/openapi/projectRoots/impl/SdkConfigurationUtil.java @@ -54,7 +54,7 @@ public class SdkConfigurationUtil { public static void createSdk(@Nullable final Project project, final Sdk[] existingSdks, - final NullableConsumer onSdkCreatedCallBack, + final NullableConsumer onSdkCreatedCallBack, final boolean createIfExists, final SdkType... sdkTypes) { if (sdkTypes.length == 0) { onSdkCreatedCallBack.consume(null); @@ -72,8 +72,19 @@ public class SdkConfigurationUtil { @Override public void consume(List selectedFiles) { for (SdkType sdkType : sdkTypes) { - if (sdkType.isValidSdkHome(selectedFiles.get(0).getPath())) { - onSdkCreatedCallBack.consume(setupSdk(existingSdks, selectedFiles.get(0), sdkType, false, null, null)); + final String path = selectedFiles.get(0).getPath(); + if (sdkType.isValidSdkHome(path)) { + Sdk newSdk = null; + if (!createIfExists) { + for (Sdk sdk : existingSdks) { + if (path.equals(sdk.getHomePath())) { + newSdk = sdk; + } + } + } + if (newSdk == null) + newSdk = setupSdk(existingSdks, selectedFiles.get(0), sdkType, false, null, null); + onSdkCreatedCallBack.consume(newSdk); return; } } @@ -85,6 +96,14 @@ public class SdkConfigurationUtil { onSdkCreatedCallBack.consume(null); } }); + + } + + public static void createSdk(@Nullable final Project project, + final Sdk[] existingSdks, + final NullableConsumer onSdkCreatedCallBack, + final SdkType... sdkTypes) { + createSdk(project, existingSdks, onSdkCreatedCallBack, true, sdkTypes); } private static FileChooserDescriptor createCompositeDescriptor(final SdkType... sdkTypes) { @@ -140,7 +159,7 @@ public class SdkConfigurationUtil { final ProjectJdkImpl sdk; try { String sdkPath = sdkType.sdkPath(homeDir); - + final String sdkName = customSdkSuggestedName == null ? createUniqueSdkName(sdkType, sdkPath, sdksList) : createUniqueSdkName(customSdkSuggestedName, sdksList); @@ -313,8 +332,9 @@ public class SdkConfigurationUtil { @Nullable private static Sdk findByPath(SdkType sdkType, Sdk[] sdks, String sdkHome) { for (Sdk sdk : sdks) { - if (sdk.getSdkType() == sdkType && - FileUtil.pathsEqual(FileUtil.toSystemIndependentName(sdk.getHomePath()), FileUtil.toSystemIndependentName(sdkHome))) { + final String path = sdk.getHomePath(); + if (sdk.getSdkType() == sdkType && path != null && + FileUtil.pathsEqual(FileUtil.toSystemIndependentName(path), FileUtil.toSystemIndependentName(sdkHome))) { return sdk; } } diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java b/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java index acbfbf17e556..bedbc3e49e71 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java @@ -2,6 +2,7 @@ package com.intellij.webcore.packaging; import com.google.common.collect.Lists; import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; @@ -9,13 +10,13 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.ui.DoubleClickListener; -import com.intellij.ui.ScrollPaneFactory; +import com.intellij.ui.*; import com.intellij.ui.table.JBTable; import com.intellij.util.CatchingConsumer; import com.intellij.util.Consumer; +import com.intellij.util.IconUtil; import com.intellij.util.ObjectUtils; -import com.intellij.util.containers.*; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,20 +27,16 @@ import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.io.IOException; import java.util.*; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public class InstalledPackagesPanel extends JPanel { - protected final JButton myInstallButton; - private final JButton myUninstallButton; - private final JButton myUpgradeButton; + private final AnActionButton myUpgradeButton; + protected final AnActionButton myInstallButton; + private final AnActionButton myUninstallButton; protected final JBTable myPackagesTable; private DefaultTableModel myPackagesTableModel; @@ -51,19 +48,9 @@ public class InstalledPackagesPanel extends JPanel { private final Set myWaitingToUpgrade = ContainerUtil.newHashSet(); public InstalledPackagesPanel(Project project, PackagesNotificationPanel area) { - super(new GridBagLayout()); + super(new BorderLayout()); myProject = project; myNotificationArea = area; - myInstallButton = new JButton("Install"); - myUninstallButton = new JButton("Uninstall"); - myUpgradeButton = new JButton("Upgrade"); - myInstallButton.setMnemonic('I'); - myUninstallButton.setMnemonic('U'); - myUpgradeButton.setMnemonic('p'); - - myInstallButton.setEnabled(false); - myUninstallButton.setEnabled(false); - myUpgradeButton.setEnabled(false); myPackagesTableModel = new DefaultTableModel(new String[]{"Package", "Version", "Latest"}, 0) { @Override @@ -80,25 +67,41 @@ public class InstalledPackagesPanel extends JPanel { }; myPackagesTable.getTableHeader().setReorderingAllowed(false); - Insets anInsets = new Insets(2, 2, 2, 2); - JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myPackagesTable, - ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - scrollPane.setPreferredSize(new Dimension(500, 500)); - add(scrollPane, new GridBagConstraints(0, 0, 1, 3, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, - anInsets, 0, 0)); + myUpgradeButton = new AnActionButton("Upgrade", IconUtil.getMoveUpIcon()) { + @Override + public void actionPerformed(AnActionEvent e) { + upgradeAction(); + } + }; + final ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myPackagesTable).disableUpDownActions() + .setAddAction(new AnActionButtonRunnable() { + @Override + public void run(AnActionButton button) { + if (myPackageManagementService != null) { + ManagePackagesDialog dialog = createManagePackagesDialog(); + dialog.show(); + } + } + }) + .setRemoveAction(new AnActionButtonRunnable() { + @Override + public void run(AnActionButton button) { + uninstallAction(); + } + }) + .addExtraAction(myUpgradeButton); - addUninstallAction(); - addUpgradeAction(); + decorator.setPreferredSize(new Dimension(500, 500)); + add(decorator.createPanel()); + myInstallButton = decorator.getActionsPanel().getAnActionButton(CommonActionsPanel.Buttons.ADD); + myUninstallButton = decorator.getActionsPanel().getAnActionButton(CommonActionsPanel.Buttons.REMOVE); + myInstallButton.setEnabled(false); + myUninstallButton.setEnabled(false); + myUpgradeButton.setEnabled(false); + + myInstallButton.getTemplatePresentation().setText("Install"); + myUninstallButton.getTemplatePresentation().setText("Uninstall"); - add(myInstallButton, - new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, - anInsets, 0, 0)); - add(myUninstallButton, - new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, - anInsets, 0, 0)); - add(myUpgradeButton, - new GridBagConstraints(1, 2, 1, 1, 0.0, 1.0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, anInsets, 0, 0)); myPackagesTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override @@ -107,16 +110,6 @@ public class InstalledPackagesPanel extends JPanel { } }); - myInstallButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (myPackageManagementService != null) { - ManagePackagesDialog dialog = createManagePackagesDialog(); - dialog.show(); - } - } - }); - new DoubleClickListener() { @Override protected boolean onDoubleClick(MouseEvent e) { @@ -162,38 +155,33 @@ public class InstalledPackagesPanel extends JPanel { myPathChangedListeners.add(consumer); } - private void addUpgradeAction() { - myUpgradeButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - final int[] rows = myPackagesTable.getSelectedRows(); - if (myPackageManagementService != null) { - final Set upgradedPackages = new HashSet(); - final Set packagesShouldBePostponed = getPackagesToPostpone(); - for (int row : rows) { - final Object packageObj = myPackagesTableModel.getValueAt(row, 0); - if (packageObj instanceof InstalledPackage) { - InstalledPackage pkg = (InstalledPackage)packageObj; - final String packageName = pkg.getName(); - final String currentVersion = pkg.getVersion(); - final String availableVersion = (String)myPackagesTableModel.getValueAt(row, 2); + private void upgradeAction() { + final int[] rows = myPackagesTable.getSelectedRows(); + if (myPackageManagementService != null) { + final Set upgradedPackages = new HashSet(); + final Set packagesShouldBePostponed = getPackagesToPostpone(); + for (int row : rows) { + final Object packageObj = myPackagesTableModel.getValueAt(row, 0); + if (packageObj instanceof InstalledPackage) { + InstalledPackage pkg = (InstalledPackage)packageObj; + final String packageName = pkg.getName(); + final String currentVersion = pkg.getVersion(); + final String availableVersion = (String)myPackagesTableModel.getValueAt(row, 2); - if (packagesShouldBePostponed.contains(packageName)) { - myWaitingToUpgrade.add((InstalledPackage)packageObj); - } - else if (PackageVersionComparator.VERSION_COMPARATOR.compare(currentVersion, availableVersion) < 0) { - upgradePackage(pkg, availableVersion); - upgradedPackages.add(packageName); - } - } + if (packagesShouldBePostponed.contains(packageName)) { + myWaitingToUpgrade.add((InstalledPackage)packageObj); } - - if (myCurrentlyInstalling.isEmpty() && upgradedPackages.isEmpty() && !myWaitingToUpgrade.isEmpty()) { - upgradePostponedPackages(); + else if (PackageVersionComparator.VERSION_COMPARATOR.compare(currentVersion, availableVersion) < 0) { + upgradePackage(pkg, availableVersion); + upgradedPackages.add(packageName); } } } - }); + + if (myCurrentlyInstalling.isEmpty() && upgradedPackages.isEmpty() && !myWaitingToUpgrade.isEmpty()) { + upgradePostponedPackages(); + } + } } private void upgradePostponedPackages() { @@ -283,7 +271,7 @@ public class InstalledPackagesPanel extends JPanel { public void run() { final int[] selected = myPackagesTable.getSelectedRows(); boolean upgradeAvailable = false; - boolean canUninstall = true; + boolean canUninstall = selected.length != 0; boolean canUpgrade = true; if (myPackageManagementService != null && selected.length != 0) { for (int i = 0; i != selected.length; ++i) { @@ -322,43 +310,38 @@ public class InstalledPackagesPanel extends JPanel { return true; } - private void addUninstallAction() { - myUninstallButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - final List packages = getSelectedPackages(); - final PackageManagementService selPackageManagementService = myPackageManagementService; - if (selPackageManagementService != null) { - PackageManagementService.Listener listener = new PackageManagementService.Listener() { - @Override - public void operationStarted(String packageName) { - myPackagesTable.setPaintBusy(true); - } - - @Override - public void operationFinished(String packageName, @Nullable String errorDescription) { - myPackagesTable.clearSelection(); - updatePackages(selPackageManagementService); - myPackagesTable.setPaintBusy(false); - if (errorDescription == null) { - if (packageName != null) { - myNotificationArea.showSuccess("Package '" + packageName + "' successfully uninstalled"); - } - else { - myNotificationArea.showSuccess("Packages successfully uninstalled"); - } - } - else { - myNotificationArea.showError("Uninstall packages failed. Details...", - "Uninstall Packages Failed", - "Uninstall packages failed.\n" + errorDescription); - } - } - }; - myPackageManagementService.uninstallPackages(packages, listener); + private void uninstallAction() { + final List packages = getSelectedPackages(); + final PackageManagementService selPackageManagementService = myPackageManagementService; + if (selPackageManagementService != null) { + PackageManagementService.Listener listener = new PackageManagementService.Listener() { + @Override + public void operationStarted(String packageName) { + myPackagesTable.setPaintBusy(true); } - } - }); + + @Override + public void operationFinished(String packageName, @Nullable String errorDescription) { + myPackagesTable.clearSelection(); + updatePackages(selPackageManagementService); + myPackagesTable.setPaintBusy(false); + if (errorDescription == null) { + if (packageName != null) { + myNotificationArea.showSuccess("Package '" + packageName + "' successfully uninstalled"); + } + else { + myNotificationArea.showSuccess("Packages successfully uninstalled"); + } + } + else { + myNotificationArea.showError("Uninstall packages failed. Details...", + "Uninstall Packages Failed", + "Uninstall packages failed.\n" + errorDescription); + } + } + }; + myPackageManagementService.uninstallPackages(packages, listener); + } } @NotNull diff --git a/platform/platform-impl/src/com/intellij/platform/PlatformProjectOpenProcessor.java b/platform/platform-impl/src/com/intellij/platform/PlatformProjectOpenProcessor.java index 1a655b8d1929..0e1087592934 100644 --- a/platform/platform-impl/src/com/intellij/platform/PlatformProjectOpenProcessor.java +++ b/platform/platform-impl/src/com/intellij/platform/PlatformProjectOpenProcessor.java @@ -189,7 +189,7 @@ public class PlatformProjectOpenProcessor extends ProjectOpenProcessor { if (project == null) return null; ProjectBaseDirectory.getInstance(project).setBaseDir(baseDir); - final Module module = runConfigurators ? runDirectoryProjectConfigurators(baseDir, project) : null; + final Module module = runConfigurators ? runDirectoryProjectConfigurators(baseDir, project) : ModuleManager.getInstance(project).getModules()[0]; openFileFromCommandLine(project, virtualFile, line); if (!projectManager.openProject(project)) { @@ -204,7 +204,7 @@ public class PlatformProjectOpenProcessor extends ProjectOpenProcessor { return project; } - if (callback != null && runConfigurators) { + if (callback != null) { callback.projectOpened(project, module); } diff --git a/python/ide/src/com/jetbrains/python/PyIdeCommonOptionsForm.java b/python/ide/src/com/jetbrains/python/PyIdeCommonOptionsForm.java index 2636aa50c66f..94e991240001 100644 --- a/python/ide/src/com/jetbrains/python/PyIdeCommonOptionsForm.java +++ b/python/ide/src/com/jetbrains/python/PyIdeCommonOptionsForm.java @@ -224,7 +224,7 @@ public class PyIdeCommonOptionsForm implements AbstractPyCommonOptionsForm { } public void updateSdkList(boolean preserveSelection, PyConfigurableInterpreterList myInterpreterList) { - myPythonSdks = myInterpreterList.getAllPythonSdks(); + myPythonSdks = myInterpreterList.getAllPythonSdks(myProject); Sdk selection = preserveSelection ? (Sdk)myInterpreterComboBox.getSelectedItem() : null; if (!myPythonSdks.contains(selection)) { selection = null; @@ -266,7 +266,7 @@ public class PyIdeCommonOptionsForm implements AbstractPyCommonOptionsForm { return myPathMappingsComponent.getMappingSettings(); } else { - return null; + return new PathMappingSettings(); } } diff --git a/python/ide/src/com/jetbrains/python/PythonSdkChooserCombo.java b/python/ide/src/com/jetbrains/python/PythonSdkChooserCombo.java index a0d2fd08312b..b0790146e6f6 100644 --- a/python/ide/src/com/jetbrains/python/PythonSdkChooserCombo.java +++ b/python/ide/src/com/jetbrains/python/PythonSdkChooserCombo.java @@ -15,18 +15,21 @@ */ package com.jetbrains.python; -import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.SdkType; import com.intellij.openapi.projectRoots.impl.SdkListCellRenderer; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.IconLoader; import com.intellij.ui.CollectionComboBoxModel; import com.intellij.ui.ComboboxWithBrowseButton; +import com.intellij.util.NullableConsumer; import com.intellij.util.containers.ContainerUtil; -import com.jetbrains.python.configuration.PythonSdkConfigurable; -import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; +import com.jetbrains.python.configuration.PythonSdkDetailsDialog; +import com.jetbrains.python.sdk.PyDetectedSdk; import com.jetbrains.python.sdk.PythonSdkType; +import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.event.ActionEvent; @@ -39,10 +42,7 @@ import java.util.List; public class PythonSdkChooserCombo extends ComboboxWithBrowseButton { private final List myChangedListeners = ContainerUtil.createLockFreeCopyOnWriteList(); - //public PythonSdkChooserCombo(final Condition acceptableSdkCondition) { - // this(PythonSdkType.getAllSdks(), acceptableSdkCondition); - //} - + @SuppressWarnings("unchecked") public PythonSdkChooserCombo(final Project project, List sdks, final Condition acceptableSdkCondition) { Sdk initialSelection = null; for (Sdk sdk : sdks) { @@ -51,26 +51,30 @@ public class PythonSdkChooserCombo extends ComboboxWithBrowseButton { break; } } - getComboBox().setModel(new CollectionComboBoxModel(sdks, initialSelection)); - getComboBox().setRenderer(new SdkListCellRenderer("") { + final JComboBox comboBox = getComboBox(); + comboBox.setModel(new CollectionComboBoxModel(sdks, initialSelection)); + comboBox.setRenderer(new SdkListCellRenderer("") { @Override protected Icon getSdkIcon(Sdk sdk) { final PythonSdkFlavor flavor = PythonSdkFlavor.getFlavor(sdk); - return flavor != null ? flavor.getIcon() : ((SdkType)sdk.getSdkType()).getIcon(); + final Icon icon = flavor != null ? flavor.getIcon() : ((SdkType)sdk.getSdkType()).getIcon(); + return sdk instanceof PyDetectedSdk ? IconLoader.getTransparentIcon(icon) : icon; } }); addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - final PythonSdkConfigurable configurable = new PythonSdkConfigurable(project); - configurable.setNewProject(true); - ShowSettingsUtil.getInstance().editConfigurable(PythonSdkChooserCombo.this, configurable); - Sdk selection = configurable.getRealSelectedSdk(); final List sdks = PythonSdkType.getAllSdks(); - getComboBox().setModel(new CollectionComboBoxModel(sdks, selection)); + PythonSdkDetailsDialog dialog = new PythonSdkDetailsDialog(project, new NullableConsumer() { + @Override + public void consume(@Nullable Sdk sdk) { + comboBox.setModel(new CollectionComboBoxModel(sdks, sdk)); + } + }); + dialog.show(); notifyChanged(e); } }); - getComboBox().addActionListener(new ActionListener() { + comboBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { notifyChanged(e); } @@ -83,6 +87,7 @@ public class PythonSdkChooserCombo extends ComboboxWithBrowseButton { } } + @SuppressWarnings("UnusedDeclaration") public void addChangedListener(ActionListener listener) { myChangedListeners.add(listener); } diff --git a/python/ide/src/com/jetbrains/python/configuration/AddVEnvOptionsDialog.form b/python/ide/src/com/jetbrains/python/configuration/AddVEnvOptionsDialog.form index 78c5657ecc11..5d2cd592e325 100644 --- a/python/ide/src/com/jetbrains/python/configuration/AddVEnvOptionsDialog.form +++ b/python/ide/src/com/jetbrains/python/configuration/AddVEnvOptionsDialog.form @@ -1,6 +1,6 @@
- + @@ -8,23 +8,14 @@ - - - - - - - - - - + - + diff --git a/python/ide/src/com/jetbrains/python/configuration/AddVEnvOptionsDialog.java b/python/ide/src/com/jetbrains/python/configuration/AddVEnvOptionsDialog.java index b4e5b8eac767..d894191369af 100644 --- a/python/ide/src/com/jetbrains/python/configuration/AddVEnvOptionsDialog.java +++ b/python/ide/src/com/jetbrains/python/configuration/AddVEnvOptionsDialog.java @@ -25,7 +25,6 @@ import java.awt.*; * @author yole */ public class AddVEnvOptionsDialog extends DialogWrapper { - private JBCheckBox myUseForThisProjectJBCheckBox; private JBCheckBox myMakeAvailableToAllJBCheckBox; private JPanel myMainPanel; @@ -40,10 +39,6 @@ public class AddVEnvOptionsDialog extends DialogWrapper { return myMainPanel; } - public boolean useForThisProject() { - return myUseForThisProjectJBCheckBox.isSelected(); - } - public boolean makeAvailableToAll() { return myMakeAvailableToAllJBCheckBox.isSelected(); } diff --git a/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.form b/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.form deleted file mode 100644 index 79a648b213a2..000000000000 --- a/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.form +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java b/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java index e4f22dd51e60..0b04517dadba 100644 --- a/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java +++ b/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java @@ -16,6 +16,7 @@ package com.jetbrains.python.configuration; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorFactory; import com.intellij.openapi.editor.ex.EditorEx; @@ -27,68 +28,218 @@ import com.intellij.openapi.options.UnnamedConfigurable; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.SdkModel; +import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil; import com.intellij.openapi.projectRoots.impl.SdkListCellRenderer; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.ModuleRootModificationUtil; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel; +import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.CollectionComboBoxModel; +import com.intellij.ui.awt.RelativePoint; +import com.intellij.util.NullableConsumer; +import com.intellij.util.ui.UIUtil; +import com.intellij.webcore.packaging.PackagesNotificationPanel; +import com.jetbrains.python.packaging.ui.PyInstalledPackagesPanel; +import com.jetbrains.python.packaging.ui.PyPackageManagementService; import com.jetbrains.python.psi.LanguageLevel; +import com.jetbrains.python.sdk.PyDetectedSdk; import com.jetbrains.python.sdk.PySdkListCellRenderer; +import com.jetbrains.python.sdk.PythonSdkDetailsStep; +import com.jetbrains.python.sdk.PythonSdkType; import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; +import icons.PythonIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; import java.util.List; -/** - * @author yole - */ public class PyActiveSdkConfigurable implements UnnamedConfigurable { - private JPanel myPanel; + private JPanel myMainPanel; private final Project myProject; @Nullable private final Module myModule; - private JComboBox mySdkCombo; - private JPanel myConfigureInterpretersPanel; + private MySdkModelListener mySdkModelListener; + private Sdk myAddedSdk = null; + private PyConfigurableInterpreterList myInterpreterList; private ProjectSdksModel myProjectSdksModel; - private MyListener myListener; + private ComboBox mySdkCombo; + private PyInstalledPackagesPanel myPackagesPanel; + private JButton myDetailsButton; + private static final String SHOW_ALL = "Show All"; + private NullableConsumer myDetailsCallback; - public PyActiveSdkConfigurable(Project project) { + public PyActiveSdkConfigurable(@NotNull Project project) { myModule = null; myProject = project; - init(); + layoutPanel(); + initContent(); } public PyActiveSdkConfigurable(@NotNull Module module) { myModule = module; myProject = module.getProject(); - init(); + layoutPanel(); + initContent(); } - private void init() { + private void initContent() { + myInterpreterList = PyConfigurableInterpreterList.getInstance(myProject); + myInterpreterList.setSdkCombo(mySdkCombo); + + myProjectSdksModel = myInterpreterList.getModel(); + mySdkModelListener = new MySdkModelListener(this); + myProjectSdksModel.addListener(mySdkModelListener); + + mySdkCombo.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + final Sdk selectedSdk = (Sdk)mySdkCombo.getSelectedItem(); + myPackagesPanel.updatePackages(selectedSdk != null ? new PyPackageManagementService(myProject, selectedSdk) : null); + } + }); + myDetailsCallback = new NullableConsumer() { + + @Override + public void consume(@Nullable Sdk sdk) { + if (sdk instanceof PyDetectedSdk) { + final Sdk addedSdk = SdkConfigurationUtil.setupSdk(myProjectSdksModel.getSdks(), sdk.getHomeDirectory(), + PythonSdkType.getInstance(), true, + null, null); + myAddedSdk = addedSdk; + myProjectSdksModel.addSdk(addedSdk); + myProjectSdksModel.removeSdk(sdk); + mySdkCombo.setSelectedItem(addedSdk); + } + else if (getSdk() != sdk) { + mySdkCombo.setSelectedItem(sdk); + } + } + }; + + myDetailsButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + PythonSdkDetailsStep + .show(myProject, myProjectSdksModel.getSdks(), + myModule == null ? new PythonSdkDetailsDialog(myProject, myDetailsCallback) : + new PythonSdkDetailsDialog(myModule, myDetailsCallback), + RelativePoint.fromScreen(myDetailsButton.getLocationOnScreen()), true, + new NullableConsumer() { + @Override + public void consume(Sdk sdk) { + if (sdk == null) return; + if (myProjectSdksModel.findSdk(sdk) == null) { + myProjectSdksModel.addSdk(sdk); + myAddedSdk = sdk; + } + updateSdkList(false); + mySdkCombo.getModel().setSelectedItem(sdk); + myPackagesPanel.updatePackages(new PyPackageManagementService(myProject, sdk)); + } + } + ); + } + } + ); + + } + + private void layoutPanel() { + final GridBagLayout layout = new GridBagLayout(); + myMainPanel = new JPanel(layout); + final JLabel interpreterLabel = new JLabel("Project Interpreter:"); + final JLabel emptyLabel = new JLabel(" "); + mySdkCombo = new ComboBox() { + @Override + public void setSelectedItem(Object item) { + if (SHOW_ALL.equals(item)) { + PythonSdkDetailsDialog options = myModule == null ? new PythonSdkDetailsDialog(myProject, myDetailsCallback) : + new PythonSdkDetailsDialog(myModule, myDetailsCallback); + options.show(); + return; + } + if (!PySdkListCellRenderer.SEPARATOR.equals(item)) + super.setSelectedItem(item); + } + }; + mySdkCombo.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE); mySdkCombo.setRenderer(new SdkListCellRenderer("")); - myInterpreterList = PyConfigurableInterpreterList.getInstance(myProject); - myProjectSdksModel = myInterpreterList.getModel(); - myListener = new MyListener(this); - myProjectSdksModel.addListener(myListener); - myConfigureInterpretersPanel.add(new PyConfigureInterpretersLinkPanel(myPanel), BorderLayout.CENTER); - myInterpreterList.setSdkCombo(mySdkCombo); + final PackagesNotificationPanel notificationsArea = new PackagesNotificationPanel(myProject); + final JComponent notificationsComponent = notificationsArea.getComponent(); + notificationsComponent.setPreferredSize(new Dimension(500, 29)); + + myDetailsButton = new JButton(); + myDetailsButton.setIcon(PythonIcons.Python.InterpreterGear); + myDetailsButton.setIconTextGap(0); + myDetailsButton.setPreferredSize(new Dimension(29, 29)); + if (((UIUtil.isUnderAquaLookAndFeel())) || UIUtil.isUnderIntelliJLaF() || UIUtil.isUnderDarcula()) { + myDetailsButton.putClientProperty("JButton.buttonType", "square"); + } + + myPackagesPanel = new PyInstalledPackagesPanel(myProject, notificationsArea); + mySdkCombo.setPreferredSize(new Dimension(250, 29)); + final GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.HORIZONTAL; + c.insets = new Insets(2,2,2,2); + + c.gridx = 0; + c.gridy = 0; + myMainPanel.add(interpreterLabel, c); + + c.gridx = 1; + c.gridy = 0; + c.weightx = 0.1; + myMainPanel.add(mySdkCombo, c); + + c.insets = new Insets(0,5,0,2); + c.gridx = 2; + c.gridy = 0; + c.weightx = 0.0; + myMainPanel.add(myDetailsButton, c); + + c.insets = new Insets(2,2,2,2); + c.gridx = 0; + c.gridy = 1; + c.gridwidth = 3; + myMainPanel.add(emptyLabel, c); + + c.gridx = 0; + c.gridy = 2; + c.weighty = 0.5; + c.gridwidth = 3; + c.gridheight = GridBagConstraints.RELATIVE; + c.fill = GridBagConstraints.BOTH; + myMainPanel.add(myPackagesPanel, c); + + c.gridheight = GridBagConstraints.REMAINDER; + c.gridx = 0; + c.gridy = 3; + c.gridwidth = 3; + + c.fill = GridBagConstraints.HORIZONTAL; + c.anchor = GridBagConstraints.SOUTH; + + myMainPanel.add(notificationsComponent, c); } @Override public JComponent createComponent() { - return myPanel; + return myMainPanel; } @Override public boolean isModified() { final Sdk sdk = getSdk(); - return sdk != myProjectSdksModel.findSdk((Sdk)mySdkCombo.getSelectedItem()); + final Sdk selectedItem = (Sdk)mySdkCombo.getSelectedItem(); + return myAddedSdk != null || selectedItem instanceof PyDetectedSdk || sdk != myProjectSdksModel.findSdk(selectedItem); } @Nullable @@ -102,28 +253,31 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable { @Override public void apply() throws ConfigurationException { - final Sdk selectedSdk = myProjectSdksModel.findSdk((Sdk)mySdkCombo.getSelectedItem()); - if (myModule == null) { - final ProjectRootManager rootManager = ProjectRootManager.getInstance(myProject); - ApplicationManager.getApplication().runWriteAction(new Runnable() { + final Sdk item = (Sdk)mySdkCombo.getSelectedItem(); + if (item instanceof PyDetectedSdk) { + ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - rootManager.setProjectSdk(selectedSdk); + final Sdk sdk = SdkConfigurationUtil.createAndAddSDK(item.getName(), PythonSdkType.getInstance()); + myProjectSdksModel.removeSdk(item); + myProjectSdksModel.addSdk(sdk); + updateSdkList(true); + mySdkCombo.setSelectedItem(sdk); + setSdk(sdk); } - }); + }, ModalityState.any()); } - else { - ModuleRootModificationUtil.setModuleSdk(myModule, selectedSdk); + if (item != null) { + myProjectSdksModel.addSdk(item); + myProjectSdksModel.apply(null, true); + mySdkCombo.setSelectedItem(item); + } + else if (myAddedSdk != null) { + myProjectSdksModel.apply(null, true); } - final Sdk prevSdk = ProjectRootManager.getInstance(myProject).getProjectSdk(); - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - ProjectRootManager.getInstance(myProject).setProjectSdk(selectedSdk); - } - }); - myProjectSdksModel.setProjectSdk(selectedSdk); + final Sdk prevSdk = ProjectRootManager.getInstance(myProject).getProjectSdk(); + final Sdk selectedSdk = setSdk(item); // update string literals if different LanguageLevel was selected if (prevSdk != null && selectedSdk != null) { @@ -141,6 +295,24 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable { rehighlightStrings(myProject); } + private Sdk setSdk(Sdk item) { + myAddedSdk = null; + final Sdk selectedSdk = myProjectSdksModel.findSdk(item); + if (myModule == null) { + final ProjectRootManager rootManager = ProjectRootManager.getInstance(myProject); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + rootManager.setProjectSdk(selectedSdk); + } + }); + } + else { + ModuleRootModificationUtil.setModuleSdk(myModule, selectedSdk); + } + return selectedSdk; + } + public static void rehighlightStrings(final @NotNull Project project) { ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override @@ -161,6 +333,14 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable { @Override public void reset() { + if (myAddedSdk != null) { + myProjectSdksModel.removeSdk(myAddedSdk); + myAddedSdk = null; + } + resetSdkList(); + } + + private void resetSdkList() { updateSdkList(false); final Sdk sdk = getSdk(); @@ -168,37 +348,64 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable { } private void updateSdkList(boolean preserveSelection) { - final List sdkList = myInterpreterList.getAllPythonSdks(); + final List sdkList = myInterpreterList.getAllPythonSdks(myProject); Sdk selection = preserveSelection ? (Sdk)mySdkCombo.getSelectedItem() : null; if (!sdkList.contains(selection)) { selection = null; } - VirtualEnvProjectFilter.removeNotMatching(myProject, sdkList); + final boolean showAll = VirtualEnvProjectFilter.removeNotMatching(myProject, sdkList); // if the selection is a non-matching virtualenv, show it anyway if (selection != null && !sdkList.contains(selection)) { sdkList.add(0, selection); } - sdkList.add(0, null); + List items = new ArrayList(); + items.add(null); + + boolean remoteSeparator = true; + boolean separator = true; + boolean detectedSeparator = true; + for (Sdk sdk : sdkList) { + if (!PythonSdkType.isVirtualEnv(sdk) && !PythonSdkType.isRemote(sdk) && !(sdk instanceof PyDetectedSdk) && separator) { + items.add(PySdkListCellRenderer.SEPARATOR); + separator = false; + } + if (PythonSdkType.isRemote(sdk) && remoteSeparator) { + items.add(PySdkListCellRenderer.SEPARATOR); + remoteSeparator = false; + } + if (sdk instanceof PyDetectedSdk && detectedSeparator) { + items.add(PySdkListCellRenderer.SEPARATOR); + detectedSeparator = false; + } + items.add(sdk); + } + + if (showAll) { + items.add(PySdkListCellRenderer.SEPARATOR); + items.add(SHOW_ALL); + } + mySdkCombo.setRenderer(new PySdkListCellRenderer()); - mySdkCombo.setModel(new CollectionComboBoxModel(sdkList, selection)); + //noinspection unchecked + mySdkCombo.setModel(new CollectionComboBoxModel(items, selection)); } @Override public void disposeUIResources() { - myProjectSdksModel.removeListener(myListener); + myProjectSdksModel.removeListener(mySdkModelListener); myInterpreterList.disposeModel(); } - private static class MyListener implements SdkModel.Listener { + private static class MySdkModelListener implements SdkModel.Listener { private final PyActiveSdkConfigurable myConfigurable; - public MyListener(PyActiveSdkConfigurable configurable) { + public MySdkModelListener(PyActiveSdkConfigurable configurable) { myConfigurable = configurable; } @Override public void sdkAdded(Sdk sdk) { - myConfigurable.reset(); + myConfigurable.resetSdkList(); } @Override diff --git a/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkModuleConfigurable.java b/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkModuleConfigurable.java index c3c299ef93d6..304424fd8f64 100644 --- a/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkModuleConfigurable.java +++ b/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkModuleConfigurable.java @@ -18,7 +18,6 @@ package com.jetbrains.python.configuration; import com.intellij.application.options.ModuleAwareProjectConfigurable; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; -import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.UnnamedConfigurable; import com.intellij.openapi.project.Project; @@ -28,17 +27,12 @@ import com.jetbrains.python.sdk.PythonSdkUpdater; import com.jetbrains.python.testing.VFSTestFrameworkListener; import org.jetbrains.annotations.NotNull; -/** - * @author yole - */ -public class PyActiveSdkModuleConfigurable extends ModuleAwareProjectConfigurable implements Configurable.Composite { +public class PyActiveSdkModuleConfigurable extends ModuleAwareProjectConfigurable { private final Project myProject; - private final Configurable mySdkConfigurable; public PyActiveSdkModuleConfigurable(Project project) { super(project, "Project Interpreter", "reference.settings.project.interpreter"); myProject = project; - mySdkConfigurable = new PythonSdkConfigurable(project); } @NotNull @@ -67,9 +61,4 @@ public class PyActiveSdkModuleConfigurable extends ModuleAwareProjectConfigurabl } PythonSdkUpdater.getInstance().updateActiveSdks(myProject, 0); } - - @Override - public Configurable[] getConfigurables() { - return new Configurable[] { mySdkConfigurable }; - } } diff --git a/python/ide/src/com/jetbrains/python/configuration/PyConfigurableInterpreterList.java b/python/ide/src/com/jetbrains/python/configuration/PyConfigurableInterpreterList.java index fbe1d0dd8d35..87b9163eb824 100644 --- a/python/ide/src/com/jetbrains/python/configuration/PyConfigurableInterpreterList.java +++ b/python/ide/src/com/jetbrains/python/configuration/PyConfigurableInterpreterList.java @@ -18,15 +18,20 @@ package com.jetbrains.python.configuration; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil; import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel; import com.intellij.openapi.util.Comparing; +import com.jetbrains.python.psi.LanguageLevel; +import com.jetbrains.python.sdk.PyDetectedSdk; +import com.jetbrains.python.sdk.PySdkUtil; +import com.jetbrains.python.sdk.PythonSdkAdditionalData; import com.jetbrains.python.sdk.PythonSdkType; +import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; +import com.jetbrains.python.sdk.flavors.VirtualEnvSdkFlavor; +import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; +import java.util.*; /** * Manages the SDK model shared between PythonSdkConfigurable and PyActiveSdkConfigurable. @@ -65,19 +70,77 @@ public class PyConfigurableInterpreterList { } } - public List getAllPythonSdks() { + public List getAllPythonSdks(@Nullable final Project project) { List result = new ArrayList(); for (Sdk sdk : getModel().getSdks()) { if (sdk.getSdkType() instanceof PythonSdkType) { result.add(sdk); } } + Collections.sort(result, new Comparator() { @Override public int compare(Sdk o1, Sdk o2) { + if (!(o1.getSdkType() instanceof PythonSdkType) || + !(o2.getSdkType() instanceof PythonSdkType)) + return -Comparing.compare(o1.getName(), o2.getName()); + + final boolean isVEnv1 = PythonSdkType.isVirtualEnv(o1); + final boolean isVEnv2 = PythonSdkType.isVirtualEnv(o2); + final boolean isRemote1 = PySdkUtil.isRemote(o1); + final boolean isRemote2 = PySdkUtil.isRemote(o2); + final PythonSdkFlavor flavor1 = PythonSdkFlavor.getFlavor(o1); + final PythonSdkFlavor flavor2 = PythonSdkFlavor.getFlavor(o2); + final LanguageLevel level1 = flavor1 != null ? flavor1.getLanguageLevel(o1) : LanguageLevel.getDefault(); + final LanguageLevel level2 = flavor2 != null ? flavor2.getLanguageLevel(o2) : LanguageLevel.getDefault(); + + if (isVEnv1) { + if (project != null && associatedWithCurrent(o1, project)) return -1; + if (isVEnv2) { + final int compare = Comparing.compare(level1, level2); + if (compare != 0) return -compare; + return Comparing.compare(o1.getName(), o2.getName()); + } + return -1; + } + if (isVEnv2) { + return 1; + } + if (isRemote1) return 1; + if (isRemote2) return -1; + + final int compare = Comparing.compare(level1, level2); + if (compare != 0) return -compare; return Comparing.compare(o1.getName(), o2.getName()); } }); + + final Collection sdkHomes = new ArrayList(); + sdkHomes.addAll(VirtualEnvSdkFlavor.INSTANCE.suggestHomePaths()); + for (PythonSdkFlavor flavor : PythonSdkFlavor.getApplicableFlavors()) { + if (flavor instanceof VirtualEnvSdkFlavor) continue; + sdkHomes.addAll(flavor.suggestHomePaths()); + } + + for (String sdkHome : SdkConfigurationUtil.filterExistingPaths(PythonSdkType.getInstance(), sdkHomes, getModel().getSdks())) { + result.add(new PyDetectedSdk(sdkHome)); + } return result; } + + private static boolean associatedWithCurrent(Sdk o1, Project project) { + final PythonSdkAdditionalData data = (PythonSdkAdditionalData)o1.getSdkAdditionalData(); + if (data != null) { + final String path = data.getAssociatedProjectPath(); + final String projectBasePath = project.getBasePath(); + if (path != null && path.equals(projectBasePath)) { + return true; + } + } + return false; + } + + public List getAllPythonSdks() { + return getAllPythonSdks(null); + } } diff --git a/python/ide/src/com/jetbrains/python/configuration/PyConfigureInterpretersLinkPanel.java b/python/ide/src/com/jetbrains/python/configuration/PyConfigureInterpretersLinkPanel.java index 08f0912eb7ca..23aa6c43b60d 100644 --- a/python/ide/src/com/jetbrains/python/configuration/PyConfigureInterpretersLinkPanel.java +++ b/python/ide/src/com/jetbrains/python/configuration/PyConfigureInterpretersLinkPanel.java @@ -16,6 +16,7 @@ package com.jetbrains.python.configuration; import com.intellij.ide.DataManager; +import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.options.newEditor.OptionsEditor; import com.intellij.ui.ClickListener; import com.intellij.ui.components.JBLabel; @@ -41,7 +42,7 @@ public class PyConfigureInterpretersLinkPanel extends JPanel { if (clickCount == 1) { final OptionsEditor optionsEditor = OptionsEditor.KEY.getData(DataManager.getInstance().getDataContext(parentPanel)); if (optionsEditor != null) { - PythonSdkConfigurable configurable = optionsEditor.findConfigurable(PythonSdkConfigurable.class); + SearchableConfigurable configurable = optionsEditor.findConfigurableById(PyActiveSdkModuleConfigurable.class.getName()); if (configurable != null) { optionsEditor.clearSearchAndSelect(configurable); } diff --git a/python/ide/src/com/jetbrains/python/configuration/PythonSdkConfigurable.form b/python/ide/src/com/jetbrains/python/configuration/PythonSdkConfigurable.form deleted file mode 100644 index b1a81e8c2a8a..000000000000 --- a/python/ide/src/com/jetbrains/python/configuration/PythonSdkConfigurable.form +++ /dev/null @@ -1,32 +0,0 @@ - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/python/ide/src/com/jetbrains/python/configuration/PythonSdkConfigurable.java b/python/ide/src/com/jetbrains/python/configuration/PythonSdkDetailsDialog.java similarity index 52% rename from python/ide/src/com/jetbrains/python/configuration/PythonSdkConfigurable.java rename to python/ide/src/com/jetbrains/python/configuration/PythonSdkDetailsDialog.java index abdfd47e64e3..96d6000573b3 100644 --- a/python/ide/src/com/jetbrains/python/configuration/PythonSdkConfigurable.java +++ b/python/ide/src/com/jetbrains/python/configuration/PythonSdkDetailsDialog.java @@ -15,41 +15,31 @@ */ package com.jetbrains.python.configuration; -import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import com.intellij.CommonBundle; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleManager; -import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; -import com.intellij.openapi.projectRoots.*; -import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.SdkModel; +import com.intellij.openapi.projectRoots.SdkModificator; import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel; +import com.intellij.openapi.ui.DialogBuilder; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.remotesdk.RemoteCredentials; import com.intellij.ui.*; import com.intellij.ui.components.JBList; -import com.intellij.ui.components.JBTabbedPane; -import com.intellij.util.Consumer; import com.intellij.util.NullableConsumer; import com.intellij.util.NullableFunction; import com.intellij.util.containers.FactoryMap; -import com.intellij.webcore.packaging.PackagesNotificationPanel; -import com.jetbrains.python.packaging.ui.PyInstalledPackagesPanel; -import com.jetbrains.python.packaging.ui.PyPackageManagementService; import com.jetbrains.python.remote.PythonRemoteInterpreterManager; import com.jetbrains.python.sdk.*; import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; @@ -63,19 +53,12 @@ import java.awt.*; import java.util.*; import java.util.List; -public class PythonSdkConfigurable implements Configurable, Configurable.NoScroll { - private JPanel myPanel; +public class PythonSdkDetailsDialog extends DialogWrapper { + private JPanel myMainPanel; private JList mySdkList; - private JPanel mySplitterHolder; - private PackagesNotificationPanel myNotificationsArea; - private JPanel myNotificationsPlaceholder; - private PythonPathEditor myPathEditor; private boolean mySdkListChanged = false; - private boolean myMakeActiveAdded = false; - private Sdk myAddedSdk; private final PyConfigurableInterpreterList myInterpreterList; private final ProjectSdksModel myProjectSdksModel; - private final PyInstalledPackagesPanel myPackagesPanel; private Map myModificators = new FactoryMap() { @Override @@ -84,25 +67,43 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol } }; private Set myModifiedModificators = new HashSet(); - private Sdk myPreviousSelection; - private boolean myFirstReset; private final Project myProject; - private boolean myNewProject = false; - private boolean myShowOtherProjectVirtualenvs = false; + private boolean myShowOtherProjectVirtualenvs = true; + private final Module myModule; + private NullableConsumer myShowMoreCallback; - public void setNewProject(final boolean newProject) { - myNewProject = newProject; - } + public PythonSdkDetailsDialog(Project project, NullableConsumer showMoreCallback) { + super(project); + myModule = null; - public PythonSdkConfigurable(Project project) { + setTitle("Project Interpreters"); + myShowMoreCallback = showMoreCallback; myProject = project; myInterpreterList = PyConfigurableInterpreterList.getInstance(myProject); myProjectSdksModel = myInterpreterList.getModel(); - myFirstReset = true; + init(); + updateOkButton(); + } + public PythonSdkDetailsDialog(Module module, NullableConsumer showMoreCallback) { + super(module.getProject()); + myModule = module; + setTitle("Project Interpreters"); + myShowMoreCallback = showMoreCallback; + myProject = module.getProject(); + myInterpreterList = PyConfigurableInterpreterList.getInstance(myProject); + myProjectSdksModel = myInterpreterList.getModel(); + init(); + updateOkButton(); + } + + @Nullable + @Override + protected JComponent createCenterPanel() { mySdkList = new JBList(); + //noinspection unchecked mySdkList.setCellRenderer(new PySdkListCellRenderer("", myModificators)); mySdkList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); @@ -111,65 +112,32 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol @Override public void run(AnActionButton button) { addSdk(button); + updateOkButton(); } }) .setEditAction(new AnActionButtonRunnable() { @Override public void run(AnActionButton button) { editSdk(); + updateOkButton(); } }) .setRemoveAction(new AnActionButtonRunnable() { @Override public void run(AnActionButton button) { removeSdk(); + updateOkButton(); } }) - .addExtraAction(new CreateVirtualEnvButton()) - .addExtraAction(new ToggleVirtualEnvFilterButton()); - - - final Splitter splitter = new Splitter(true); - /* - final JScrollPane sdkListPane = ScrollPaneFactory.createScrollPane(mySdkList, - ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - sdkListPane.setPreferredSize(new Dimension(10, 10)); - */ - splitter.setFirstComponent(decorator.createPanel()); - - myPathEditor = - new PythonPathEditor("Classes", OrderRootType.CLASSES, FileChooserDescriptorFactory.createAllButJarContentsDescriptor()) { - @Override - protected void onReloadButtonClicked() { - reloadSdk(); - } - }; - - myNotificationsArea = new PackagesNotificationPanel(project); - myNotificationsPlaceholder.add(myNotificationsArea.getComponent(), BorderLayout.CENTER); - - final JBTabbedPane tabbedPane = new JBTabbedPane(SwingConstants.TOP); - myPackagesPanel = new PyInstalledPackagesPanel(project, myNotificationsArea); - tabbedPane.addTab("Packages", myPackagesPanel); - - JPanel panel1 = new JPanel(new GridBagLayout()); - Insets anInsets1 = new Insets(2, 2, 2, 2); - JScrollPane scrollPane1 = ScrollPaneFactory.createScrollPane(myPathEditor.createComponent(), - ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - scrollPane1.setPreferredSize(new Dimension(500, 500)); - panel1.add(scrollPane1, new GridBagConstraints(0, 0, 1, 8, 1.0, 1.0, - GridBagConstraints.CENTER, - GridBagConstraints.BOTH, - anInsets1, 0, 0)); - - tabbedPane.addTab("Paths", panel1); - - splitter.setSecondComponent(tabbedPane); - mySplitterHolder.add(splitter, BorderLayout.CENTER); + .addExtraAction(new ToggleVirtualEnvFilterButton()) + .addExtraAction(new ShowPathButton()) + .addExtraAction(new GenerateSkeletonsButton()); + decorator.setPreferredSize(new Dimension(600, 500)); + myMainPanel = decorator.createPanel(); + refreshSdkList(); addListeners(); + return myMainPanel; } private void addListeners() { @@ -191,99 +159,49 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol public void sdkHomeSelected(Sdk sdk, String newSdkHome) { } }); - myPackagesPanel.addPathChangedListener(new Consumer() { - @Override - public void consume(Sdk sdk) { - updateSdkPaths(sdk); - } - }); - - myNotificationsArea.addLinkHandler(PyInstalledPackagesPanel.CREATE_VENV, new Runnable() { - @Override - public void run() { - createVirtualEnv(getSelectedSdk()); - } - }); mySdkList.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent event) { + updateOkButton(); updateUI(getSelectedSdk()); } }); } private void updateUI(final Sdk selectedSdk) { - if (myPreviousSelection != null) { - saveSdkPaths(myPreviousSelection); - } myProjectSdksModel.setProjectSdk(selectedSdk); - updateSdkPaths(selectedSdk); - myPreviousSelection = selectedSdk; - myPackagesPanel.updatePackages(selectedSdk == null ? null : new PyPackageManagementService(myProject, selectedSdk)); - - if (selectedSdk != null) { - myPackagesPanel.updateNotifications(selectedSdk); - } - } - - private void createVirtualEnv(Sdk sdk) { - CreateVirtualEnvDialog.VirtualEnvCallback callback = new CreateVirtualEnvDialog.VirtualEnvCallback() { - @Override - public void virtualEnvCreated(Sdk sdk, boolean associateWithProject, boolean setAsProjectInterpreter) { - PythonSdkType.setupSdkPaths(sdk, myProject, null); - if (associateWithProject) { - SdkAdditionalData additionalData = sdk.getSdkAdditionalData(); - if (additionalData == null) { - additionalData = new PythonSdkAdditionalData(PythonSdkFlavor.getFlavor(sdk.getHomePath())); - ((ProjectJdkImpl)sdk).setSdkAdditionalData(additionalData); - } - if (myNewProject) { - ((PythonSdkAdditionalData)additionalData).associateWithNewProject(); - } - else { - ((PythonSdkAdditionalData)additionalData).associateWithProject(myProject); - } - } - myMakeActiveAdded = setAsProjectInterpreter; - addCreatedSdk(sdk, true); - } - }; - final List allSdks = PyConfigurableInterpreterList.getInstance(myProject).getAllPythonSdks(); - final CreateVirtualEnvDialog dialog = new CreateVirtualEnvDialog(myProject, myNewProject, allSdks, sdk); - dialog.show(); - if (dialog.isOK()) { - dialog.createVirtualEnv(allSdks, callback); - } - } - - public String getDisplayName() { - return "Python Interpreters"; - } - - public String getHelpTopic() { - return "python_interpreter"; - } - - public JComponent createComponent() { - return myPanel; - } - - public boolean isModified() { - return mySdkListChanged || - myProjectSdksModel.isModified() || - myPathEditor.isModified() || - !myModifiedModificators.isEmpty(); } @Nullable - private String getSelectedSdkName() { - final Sdk selectedSdk = (Sdk)mySdkList.getSelectedValue(); - return selectedSdk == null ? null : selectedSdk.getName(); + @Override + public JComponent getPreferredFocusedComponent() { + return mySdkList; + } + + public boolean isModified() { + Sdk projectSdk = getSdk(); + if (projectSdk != null) { + projectSdk = myProjectSdksModel.findSdk(projectSdk.getName()); + } + return getSelectedSdk() != projectSdk || mySdkListChanged || + myProjectSdksModel.isModified() || + !myModifiedModificators.isEmpty(); + } + + protected void updateOkButton() { + super.setOKActionEnabled(isModified()); + } + + @Override + protected void doOKAction() { + try { + apply(); + } + catch (ConfigurationException ignored) { + } + super.doOKAction(); } public void apply() throws ConfigurationException { - if (myPreviousSelection != null) { - saveSdkPaths(myPreviousSelection); - } for (SdkModificator modificator : myModifiedModificators) { modificator.commitChanges(); } @@ -291,21 +209,7 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol myModifiedModificators.clear(); myProjectSdksModel.apply(); mySdkListChanged = false; - if (myMakeActiveAdded) { - SdkConfigurationUtil.setDirectoryProjectSdk(myProject, myAddedSdk); - myProjectSdksModel.setProjectSdk(myAddedSdk); - myInterpreterList.setSelectedSdk(myAddedSdk); - } - } - - /** - * Returns the stable copy of the SDK currently selected in the SDK table. - * - * @return the selected SDK, or null if there's no selection - */ - @Nullable - public Sdk getRealSelectedSdk() { - return ProjectJdkTable.getInstance().findJdk(getSelectedSdkName()); + myShowMoreCallback.consume(getSelectedSdk()); } @Nullable @@ -313,38 +217,17 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol return (Sdk)mySdkList.getSelectedValue(); } - public void reset() { - clearModificators(); - if (myFirstReset) { - myFirstReset = false; - } - else { - myProjectSdksModel.reset(null); - } - refreshSdkList(); - final Sdk selectedSdk = getRealSelectedSdk(); - if (selectedSdk != null) { - myPackagesPanel.updateNotifications(selectedSdk); - } - } - - private void clearModificators() { - myModificators.clear(); - myModifiedModificators.clear(); - myPreviousSelection = null; - } - private void refreshSdkList() { - final List pythonSdks = myInterpreterList.getAllPythonSdks(); - Sdk projectSdk = myProjectSdksModel.getProjectSdk(); + final List pythonSdks = myInterpreterList.getAllPythonSdks(myProject); + Sdk projectSdk = getSdk(); if (!myShowOtherProjectVirtualenvs) { VirtualEnvProjectFilter.removeNotMatching(myProject, pythonSdks); } Collections.sort(pythonSdks, new PreferredSdkComparator()); + //noinspection unchecked mySdkList.setModel(new CollectionListModel(pythonSdks)); mySdkListChanged = false; - if (projectSdk == null) projectSdk = getSdk(); if (projectSdk != null) { projectSdk = myProjectSdksModel.findSdk(projectSdk.getName()); mySdkList.clearSelection(); @@ -352,24 +235,20 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol mySdkList.updateUI(); } } - @Nullable private Sdk getSdk() { - final Module[] modules = ModuleManager.getInstance(myProject).getModules(); - if (modules.length > 0) { - final Module module = modules[0]; - final ModuleRootManager rootManager = ModuleRootManager.getInstance(module); - return rootManager.getSdk(); + if (myModule == null) { + return ProjectRootManager.getInstance(myProject).getProjectSdk(); } - return ProjectRootManager.getInstance(myProject).getProjectSdk(); + final ModuleRootManager rootManager = ModuleRootManager.getInstance(myModule); + return rootManager.getSdk(); } private void addSdk(AnActionButton button) { - InterpreterPathChooser - .show(myProject, myProjectSdksModel.getSdks(), button.getPreferredPopupPoint(), false, new NullableConsumer() { + PythonSdkDetailsStep + .show(myProject, myProjectSdksModel.getSdks(), this, button.getPreferredPopupPoint(), false, new NullableConsumer() { @Override public void consume(Sdk sdk) { - myMakeActiveAdded = false; addCreatedSdk(sdk, false); } }); @@ -377,11 +256,9 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol private void addCreatedSdk(@Nullable final Sdk sdk, boolean newVirtualEnv) { if (sdk != null) { - myAddedSdk = sdk; boolean isVirtualEnv = PythonSdkType.isVirtualEnv(sdk); - boolean askSetAsProjectInterpreter = !myProject.isDefault() && !myNewProject; - if (askSetAsProjectInterpreter && isVirtualEnv && !newVirtualEnv) { - AddVEnvOptionsDialog dialog = new AddVEnvOptionsDialog(myPanel); + if (isVirtualEnv && !newVirtualEnv) { + AddVEnvOptionsDialog dialog = new AddVEnvOptionsDialog(myMainPanel); dialog.show(); if (dialog.getExitCode() != DialogWrapper.OK_EXIT_CODE) { return; @@ -389,20 +266,13 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol SdkModificator modificator = myModificators.get(sdk); setSdkAssociated(modificator, !dialog.makeAvailableToAll()); myModifiedModificators.add(modificator); - myMakeActiveAdded = dialog.useForThisProject(); } - myProjectSdksModel.addSdk(sdk); + final Sdk oldSdk = myProjectSdksModel.findSdk(sdk); + if (oldSdk == null) + myProjectSdksModel.addSdk(sdk); refreshSdkList(); mySdkList.setSelectedValue(sdk, true); mySdkListChanged = true; - if (askSetAsProjectInterpreter && !isVirtualEnv && !PythonSdkType.isInvalid(sdk) && !PythonSdkType.isIncompleteRemote(sdk)) { - //TODO: make native mac dialog work - myMakeActiveAdded = Messages.showIdeaMessageDialog(myProject, "Do you want to set this interpreter as Project Interpreter?", - "Project Interpreter", - new String[]{CommonBundle.getYesButtonText(), CommonBundle.getNoButtonText()}, 0, null, - null - ) == Messages.YES; - } } } @@ -521,55 +391,6 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol private void reloadSdk(Sdk currentSdk) { PythonSdkType.setupSdkPaths(myProject, null, currentSdk, myModificators.get(currentSdk)); // or must it be a RunWriteAction? - reloadSdkPaths(currentSdk); - } - - public void disposeUIResources() { - myInterpreterList.disposeModel(); - clearModificators(); - myFirstReset = true; - } - - private void saveSdkPaths(Sdk selection) { - SdkModificator modificator = myModificators.get(selection); - if (myPathEditor.isModified()) { - myPathEditor.apply(modificator); - myModifiedModificators.add(modificator); - } - } - - private void reloadSdkPaths(Sdk selection) { - List rootPaths = Lists.newArrayList(); - if (selection != null) { - Collections.addAll(rootPaths, selection.getRootProvider().getFiles(OrderRootType.CLASSES)); - myPathEditor.reload(myModificators.get(selection)); - } - else { - myPathEditor.reload(null); - } - } - - private void updateSdkPaths(final Sdk selection) { - final List rootPaths = Lists.newArrayList(); - if (selection != null) { - Collections.addAll(rootPaths, selection.getRootProvider().getFiles(OrderRootType.CLASSES)); - myPathEditor.reset(myModificators.get(selection)); - } - else { - myPathEditor.reset(null); - } - } - - private class CreateVirtualEnvButton extends AnActionButton implements DumbAware { - public CreateVirtualEnvButton() { - super("Create Virtual Environment", PythonIcons.Python.Virtualenv); - } - - @Override - public void actionPerformed(AnActionEvent e) { - Sdk selectedSdk = getSelectedSdk(); - createVirtualEnv(selectedSdk); - } } private class ToggleVirtualEnvFilterButton extends ToggleActionButton implements DumbAware { @@ -586,6 +407,73 @@ public class PythonSdkConfigurable implements Configurable, Configurable.NoScrol public void setSelected(AnActionEvent e, boolean state) { myShowOtherProjectVirtualenvs = state; refreshSdkList(); + updateOkButton(); + } + } + + private class ShowPathButton extends AnActionButton implements DumbAware { + public ShowPathButton() { + super("Show path for the selected interpreter", AllIcons.Actions.ShowAsTree); + } + + @Override + public boolean isEnabled() { + return !(getSelectedSdk() instanceof PyDetectedSdk); + } + + @Override + public void actionPerformed(AnActionEvent e) { + DialogBuilder dialog = new DialogBuilder(myProject); + + final PythonPathEditor editor = + new PythonPathEditor("Classes", OrderRootType.CLASSES, FileChooserDescriptorFactory.createAllButJarContentsDescriptor()) { + @Override + protected void onReloadButtonClicked() { + reloadSdk(); + } + }; + final JComponent component = editor.createComponent(); + component.setPreferredSize(new Dimension(600, 400)); + component.setBorder(IdeBorderFactory.createBorder(SideBorder.ALL)); + dialog.setCenterPanel(component); + final Sdk sdk = getSelectedSdk(); + editor.reload(sdk != null ? sdk.getSdkModificator(): null); + + dialog.setTitle("Interpreter Paths"); + dialog.show(); + updateOkButton(); + } + } + + private class GenerateSkeletonsButton extends AnActionButton implements DumbAware { + public GenerateSkeletonsButton() { + super("Generate skeletons for the selected interpreter", PythonIcons.Python.Skeleton); + } + + @Override + public boolean isEnabled() { + return (getSelectedSdk() instanceof PyDetectedSdk); + } + + @Override + public void actionPerformed(AnActionEvent e) { + final Sdk sdk = getSelectedSdk(); + if (sdk instanceof PyDetectedSdk) { + try { + myProjectSdksModel.apply(); + } + catch (ConfigurationException ignored) { + } + + final Sdk addedSdk = SdkConfigurationUtil.setupSdk(myProjectSdksModel.getSdks(), sdk.getHomeDirectory(), + PythonSdkType.getInstance(), true, + null, null); + myProjectSdksModel.addSdk(addedSdk); + myProjectSdksModel.removeSdk(sdk); + refreshSdkList(); + mySdkList.setSelectedValue(addedSdk, true); + updateOkButton(); + } } } } diff --git a/python/ide/src/com/jetbrains/python/configuration/VirtualEnvProjectFilter.java b/python/ide/src/com/jetbrains/python/configuration/VirtualEnvProjectFilter.java index 1caf4c31d7e0..c8dc610f9546 100644 --- a/python/ide/src/com/jetbrains/python/configuration/VirtualEnvProjectFilter.java +++ b/python/ide/src/com/jetbrains/python/configuration/VirtualEnvProjectFilter.java @@ -50,13 +50,14 @@ public class VirtualEnvProjectFilter implements Predicate { return false; } - public static void removeNotMatching(Project project, List sdks) { + public static boolean removeNotMatching(Project project, List sdks) { if (project != null) { final String basePath = project.getBasePath(); if (basePath != null) { - Iterables.removeIf(sdks, new VirtualEnvProjectFilter(FileUtil.toSystemIndependentName(basePath))); + return Iterables.removeIf(sdks, new VirtualEnvProjectFilter(FileUtil.toSystemIndependentName(basePath))); } } + return false; } public static void removeAllAssociated(List sdks) { diff --git a/python/ide/src/com/jetbrains/python/newProject/PythonNewDirectoryProjectAction.java b/python/ide/src/com/jetbrains/python/newProject/PythonNewDirectoryProjectAction.java index 4496b6d4d854..4d638eee4101 100644 --- a/python/ide/src/com/jetbrains/python/newProject/PythonNewDirectoryProjectAction.java +++ b/python/ide/src/com/jetbrains/python/newProject/PythonNewDirectoryProjectAction.java @@ -27,6 +27,7 @@ import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.platform.DirectoryProjectGenerator; import com.intellij.platform.NewDirectoryProjectAction; +import com.jetbrains.python.sdk.PyDetectedSdk; import com.jetbrains.python.sdk.PythonSdkAdditionalData; import com.jetbrains.python.sdk.PythonSdkType; @@ -48,6 +49,9 @@ public class PythonNewDirectoryProjectAction extends NewDirectoryProjectAction { dlg.show(); if (dlg.getExitCode() != DialogWrapper.OK_EXIT_CODE) return; mySdk = dlg.getSdk(); + if (mySdk instanceof PyDetectedSdk) { + mySdk = SdkConfigurationUtil.createAndAddSDK(mySdk.getName(), PythonSdkType.getInstance()); + } myInstallFramework = dlg.installFramework(); Project newProject = generateProject(project, dlg); if (newProject != null) { diff --git a/python/ide/src/com/jetbrains/python/newProject/PythonNewDirectoryProjectDialog.java b/python/ide/src/com/jetbrains/python/newProject/PythonNewDirectoryProjectDialog.java index 4b05b73434e1..8db6add9d056 100644 --- a/python/ide/src/com/jetbrains/python/newProject/PythonNewDirectoryProjectDialog.java +++ b/python/ide/src/com/jetbrains/python/newProject/PythonNewDirectoryProjectDialog.java @@ -31,6 +31,7 @@ import com.intellij.ui.ComboboxWithBrowseButton; import com.intellij.ui.components.JBCheckBox; import com.intellij.ui.components.JBLabel; import com.jetbrains.python.PythonSdkChooserCombo; +import com.jetbrains.python.configuration.PyConfigurableInterpreterList; import com.jetbrains.python.configuration.VirtualEnvProjectFilter; import com.jetbrains.python.packaging.PyExternalProcessException; import com.jetbrains.python.packaging.PyPackage; @@ -38,7 +39,6 @@ import com.jetbrains.python.packaging.PyPackageManager; import com.jetbrains.python.packaging.PyPackageManagerImpl; import com.jetbrains.python.remote.PythonRemoteInterpreterManager; import com.jetbrains.python.remote.RemoteProjectSettings; -import com.jetbrains.python.sdk.PreferredSdkComparator; import com.jetbrains.python.sdk.PythonSdkType; import com.jetbrains.python.sdk.flavors.JythonSdkFlavor; import com.jetbrains.python.sdk.flavors.PyPySdkFlavor; @@ -53,7 +53,6 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.util.Collections; import java.util.List; /** @@ -69,9 +68,8 @@ public class PythonNewDirectoryProjectDialog extends NewDirectoryProjectDialog { super(project); myProject = project; - final List sdks = PythonSdkType.getAllSdks(); + final List sdks = PyConfigurableInterpreterList.getInstance(myProject).getAllPythonSdks(); VirtualEnvProjectFilter.removeAllAssociated(sdks); - Collections.sort(sdks, PreferredSdkComparator.INSTANCE); final Sdk preferred = sdks.isEmpty() ? null : sdks.iterator().next(); mySdkCombo = new PythonSdkChooserCombo(project, sdks, new Condition() { @Override diff --git a/python/resources/icon-robots.txt b/python/resources/icon-robots.txt index e1ca58536c30..e9cf8c7fee89 100644 --- a/python/resources/icon-robots.txt +++ b/python/resources/icon-robots.txt @@ -1,2 +1,3 @@ # Only has artwork and tips stuff -skip: * +skip: *.png +skip: tips diff --git a/python/resources/icons/com/jetbrains/python/interpreterGear.png b/python/resources/icons/com/jetbrains/python/interpreterGear.png new file mode 100644 index 000000000000..448474acb643 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/interpreterGear.png differ diff --git a/python/resources/icons/com/jetbrains/python/interpreterGear@2x.png b/python/resources/icons/com/jetbrains/python/interpreterGear@2x.png new file mode 100644 index 000000000000..fe190f2412aa Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/interpreterGear@2x.png differ diff --git a/python/resources/icons/com/jetbrains/python/interpreterGear@2x_dark.png b/python/resources/icons/com/jetbrains/python/interpreterGear@2x_dark.png new file mode 100644 index 000000000000..2d77aac65fa5 Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/interpreterGear@2x_dark.png differ diff --git a/python/resources/icons/com/jetbrains/python/interpreterGear_dark.png b/python/resources/icons/com/jetbrains/python/interpreterGear_dark.png new file mode 100644 index 000000000000..44e79af9551d Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/interpreterGear_dark.png differ diff --git a/python/resources/icons/com/jetbrains/python/skeleton.png b/python/resources/icons/com/jetbrains/python/skeleton.png new file mode 100644 index 000000000000..5b50602ef01b Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/skeleton.png differ diff --git a/python/resources/icons/com/jetbrains/python/skeleton@2x.png b/python/resources/icons/com/jetbrains/python/skeleton@2x.png new file mode 100644 index 000000000000..313b0249fffa Binary files /dev/null and b/python/resources/icons/com/jetbrains/python/skeleton@2x.png differ diff --git a/python/src/META-INF/pycharm-core.xml b/python/src/META-INF/pycharm-core.xml index 7a4a9c694cda..0c1457d48049 100644 --- a/python/src/META-INF/pycharm-core.xml +++ b/python/src/META-INF/pycharm-core.xml @@ -31,7 +31,6 @@ - diff --git a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.form b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.form index bd84b37f8011..ea6172e1faac 100644 --- a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.form +++ b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.form @@ -1,6 +1,6 @@
- + @@ -16,7 +16,7 @@ - + @@ -60,30 +60,22 @@ - + - + - + + - - - - - - - - - diff --git a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java index e86fca40be89..165d73732043 100644 --- a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java +++ b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java @@ -15,6 +15,8 @@ */ package com.jetbrains.python.sdk; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; import com.intellij.facet.ui.FacetEditorValidator; import com.intellij.facet.ui.FacetValidatorsManager; import com.intellij.openapi.application.Application; @@ -67,18 +69,16 @@ public class CreateVirtualEnvDialog extends IdeaDialog { private JTextField myName; private JBCheckBox mySitePackagesCheckBox; private JBCheckBox myMakeAvailableToAllProjectsCheckbox; - private JBCheckBox mySetAsProjectInterpreterCheckbox; @Nullable private Project myProject; private String myInitialPath; public interface VirtualEnvCallback { - void virtualEnvCreated(Sdk sdk, boolean associateWithProject, boolean setAsProjectInterpreter); + void virtualEnvCreated(Sdk sdk, boolean associateWithProject); } - private static void setupVirtualEnvSdk(List allSdks, + private void setupVirtualEnvSdk(List allSdks, final String path, boolean associateWithProject, - final boolean makeActive, VirtualEnvCallback callback) { final VirtualFile sdkHome = ApplicationManager.getApplication().runWriteAction(new Computable() { @@ -92,46 +92,45 @@ public class CreateVirtualEnvDialog extends IdeaDialog { SdkConfigurationUtil.createUniqueSdkName(PythonSdkType.getInstance(), sdkHome.getPath(), allSdks); final ProjectJdkImpl sdk = new ProjectJdkImpl(name, PythonSdkType.getInstance()); sdk.setHomePath(sdkHome.getPath()); - callback.virtualEnvCreated(sdk, associateWithProject, makeActive); + callback.virtualEnvCreated(sdk, associateWithProject); + PythonSdkType.setupSdkPaths(sdk, myProject, null); } } public CreateVirtualEnvDialog(Project project, - boolean isNewProject, final List allSdks, @Nullable Sdk suggestedBaseSdk) { super(project); - setupDialog(project, isNewProject, allSdks, suggestedBaseSdk); + setupDialog(project, allSdks, suggestedBaseSdk); } public CreateVirtualEnvDialog(Component owner, - boolean isNewProject, final List allSdks, @Nullable Sdk suggestedBaseSdk) { super(owner); - setupDialog(null, isNewProject, allSdks, suggestedBaseSdk); + setupDialog(null, allSdks, suggestedBaseSdk); } - private void setupDialog(Project project, boolean isNewProject, List allSdks, @Nullable Sdk suggestedBaseSdk) { + private void setupDialog(Project project, List allSdks, @Nullable Sdk suggestedBaseSdk) { myProject = project; init(); setTitle("Create Virtual Environment"); if (suggestedBaseSdk == null && allSdks.size() > 0) { + Iterables.removeIf(allSdks, new Predicate() { + @Override + public boolean apply(Sdk s) { + return PythonSdkType.isInvalid(s) || PythonSdkType.isVirtualEnv(s) || RemoteSdkDataHolder.isRemoteSdk(s.getHomePath()); + } + }); List sortedSdks = new ArrayList(allSdks); Collections.sort(sortedSdks, new PreferredSdkComparator()); suggestedBaseSdk = sortedSdks.get(0); } updateSdkList(allSdks, suggestedBaseSdk); - myMakeAvailableToAllProjectsCheckbox.setBorder(BorderFactory.createEmptyBorder(8, 0, 0, 0)); if (project == null || project.isDefault() || !PlatformUtils.isPyCharm()) { myMakeAvailableToAllProjectsCheckbox.setSelected(true); myMakeAvailableToAllProjectsCheckbox.setVisible(false); - mySetAsProjectInterpreterCheckbox.setSelected(false); - mySetAsProjectInterpreterCheckbox.setVisible(false); - } - else if (isNewProject) { - mySetAsProjectInterpreterCheckbox.setText("Set as project interpreter for the project being created"); } setOKActionEnabled(false); @@ -238,17 +237,7 @@ public class CreateVirtualEnvDialog extends IdeaDialog { private void updateSdkList(final List allSdks, @Nullable Sdk initialSelection) { mySdkCombo.setRenderer(new PySdkListCellRenderer()); - List baseSdks = new ArrayList(); - for (Sdk s : allSdks) { - if (!PythonSdkType.isInvalid(s) && !PythonSdkType.isVirtualEnv(s) && !RemoteSdkDataHolder.isRemoteSdk(s.getHomePath())) { - baseSdks.add(s); - } - else if (s.equals(initialSelection)){ - initialSelection = null; - } - } - - mySdkCombo.setModel(new CollectionComboBoxModel(baseSdks, initialSelection)); + mySdkCombo.setModel(new CollectionComboBoxModel(allSdks, initialSelection)); } @Override @@ -288,10 +277,6 @@ public class CreateVirtualEnvDialog extends IdeaDialog { return !myMakeAvailableToAllProjectsCheckbox.isSelected(); } - public boolean setAsProjectInterpreter() { - return mySetAsProjectInterpreterCheckbox.isSelected(); - } - @Override public JComponent getPreferredFocusedComponent() { return myName; @@ -326,7 +311,7 @@ public class CreateVirtualEnvDialog extends IdeaDialog { application.invokeLater(new Runnable() { @Override public void run() { - setupVirtualEnvSdk(allSdks, myPath, associateWithProject(), setAsProjectInterpreter(), callback); + setupVirtualEnvSdk(allSdks, myPath, associateWithProject(), callback); } }, ModalityState.any()); } diff --git a/python/src/com/jetbrains/python/sdk/PreferredSdkComparator.java b/python/src/com/jetbrains/python/sdk/PreferredSdkComparator.java index 50bc771cfb8a..ff1cea915df4 100644 --- a/python/src/com/jetbrains/python/sdk/PreferredSdkComparator.java +++ b/python/src/com/jetbrains/python/sdk/PreferredSdkComparator.java @@ -37,6 +37,12 @@ public class PreferredSdkComparator implements Comparator { if (remote1Weight != remote2Weight) { return remote2Weight - remote1Weight; } + int detectedWeight1 = o1 instanceof PyDetectedSdk ? 0 : 1; + int detectedWeight2 = o2 instanceof PyDetectedSdk ? 0 : 1; + if (detectedWeight1 != detectedWeight2) { + return detectedWeight2 - detectedWeight1; + } + int venv1weight = PythonSdkType.isVirtualEnv(o1) ? 0 : 1; int venv2weight = PythonSdkType.isVirtualEnv(o2) ? 0 : 1; if (venv1weight != venv2weight) { @@ -47,6 +53,7 @@ public class PreferredSdkComparator implements Comparator { if (flavor1weight != flavor2weight) { return flavor2weight - flavor1weight; } + return -Comparing.compare(o1.getVersionString(), o2.getVersionString()); } } diff --git a/python/src/com/jetbrains/python/sdk/PyDetectedSdk.java b/python/src/com/jetbrains/python/sdk/PyDetectedSdk.java new file mode 100644 index 000000000000..098462ef44fb --- /dev/null +++ b/python/src/com/jetbrains/python/sdk/PyDetectedSdk.java @@ -0,0 +1,11 @@ +package com.jetbrains.python.sdk; + +import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl; + +public class PyDetectedSdk extends ProjectJdkImpl { + public PyDetectedSdk(String name) { + super(name, PythonSdkType.getInstance()); + setHomePath(name); + } + +} diff --git a/python/src/com/jetbrains/python/sdk/PySdkListCellRenderer.java b/python/src/com/jetbrains/python/sdk/PySdkListCellRenderer.java index 9065d371bd94..988622484b97 100644 --- a/python/src/com/jetbrains/python/sdk/PySdkListCellRenderer.java +++ b/python/src/com/jetbrains/python/sdk/PySdkListCellRenderer.java @@ -19,6 +19,7 @@ import com.intellij.icons.AllIcons; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.SdkModificator; import com.intellij.openapi.projectRoots.SdkType; +import com.intellij.openapi.util.IconLoader; import com.intellij.ui.LayeredIcon; import com.intellij.ui.ListCellRendererWrapper; import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; @@ -27,9 +28,10 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.Map; -public class PySdkListCellRenderer extends ListCellRendererWrapper { +public class PySdkListCellRenderer extends ListCellRendererWrapper { private final String myNullText; private final Map mySdkModifiers; + public static final String SEPARATOR = "separator"; public PySdkListCellRenderer() { myNullText = ""; @@ -42,8 +44,9 @@ public class PySdkListCellRenderer extends ListCellRendererWrapper { } @Override - public void customize(JList list, Sdk sdk, int index, boolean selected, boolean hasFocus) { - if (sdk != null) { + public void customize(JList list, Object item, int index, boolean selected, boolean hasFocus) { + if (item instanceof Sdk) { + Sdk sdk = (Sdk)item; final PythonSdkFlavor flavor = PythonSdkFlavor.getPlatformIndependentFlavor(sdk.getHomePath()); final Icon icon = flavor != null ? flavor.getIcon() : ((SdkType)sdk.getSdkType()).getIcon(); @@ -54,7 +57,6 @@ public class PySdkListCellRenderer extends ListCellRendererWrapper { else { name = sdk.getName(); } - if (PythonSdkType.isInvalid(sdk)) { setText("[invalid] " + name); setIcon(wrapIconWithWarningDecorator(icon)); @@ -63,17 +65,22 @@ public class PySdkListCellRenderer extends ListCellRendererWrapper { setText("[incomplete] " + name); setIcon(wrapIconWithWarningDecorator(icon)); } + else if (sdk instanceof PyDetectedSdk){ + setText(name); + setIcon(IconLoader.getTransparentIcon(icon)); + } else { setText(name); setIcon(icon); } } - else { + else if (SEPARATOR.equals(item)) + setSeparator(); + else if (item == null) setText(myNullText); - } } - private LayeredIcon wrapIconWithWarningDecorator(Icon icon) { + private static LayeredIcon wrapIconWithWarningDecorator(Icon icon) { final LayeredIcon layered = new LayeredIcon(2); layered.setIcon(icon, 0); // TODO: Create a separate invalid SDK overlay icon (DSGN-497) diff --git a/python/src/com/jetbrains/python/sdk/InterpreterPathChooser.java b/python/src/com/jetbrains/python/sdk/PythonSdkDetailsStep.java similarity index 50% rename from python/src/com/jetbrains/python/sdk/InterpreterPathChooser.java rename to python/src/com/jetbrains/python/sdk/PythonSdkDetailsStep.java index ab57e352a5b1..8e3f18a9ee13 100644 --- a/python/src/com/jetbrains/python/sdk/InterpreterPathChooser.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkDetailsStep.java @@ -21,102 +21,80 @@ import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.SdkAdditionalData; +import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl; import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil; +import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.ui.popup.ListPopup; -import com.intellij.openapi.ui.popup.ListPopupStep; -import com.intellij.openapi.ui.popup.PopupStep; +import com.intellij.openapi.ui.popup.*; import com.intellij.openapi.ui.popup.util.BaseListPopupStep; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.ui.awt.RelativePoint; import com.intellij.util.NullableConsumer; -import com.intellij.util.SystemProperties; import com.jetbrains.python.remote.PythonRemoteInterpreterManager; import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.*; import java.awt.*; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.List; -/** -* @author yole -*/ -public class InterpreterPathChooser extends BaseListPopupStep { +public class PythonSdkDetailsStep extends BaseListPopupStep { + private static DialogWrapper myMore; private final Project myProject; private final Component myOwnerComponent; private final Sdk[] myExistingSdks; private final NullableConsumer myCallback; - private static final String LOCAL = "Local..."; - private static final String REMOTE = "Remote..."; - private static final String VIRTUALENV = "Create VirtualEnv..."; + private static final String LOCAL = "Add Local"; + private static final String REMOTE = "Add Remote"; + private static final String VIRTUALENV = "Create VirtualEnv"; + private static final String MORE = "More..."; public static void show(final Project project, final Sdk[] existingSdks, - final RelativePoint popupPoint, - final boolean showVirtualEnv, + DialogWrapper component, final RelativePoint popupPoint, + final boolean showMore, final NullableConsumer callback) { - ListPopupStep sdkHomesStep = new InterpreterPathChooser(project, popupPoint.getComponent(), existingSdks, showVirtualEnv, callback); + myMore = component; + final ListPopupStep sdkHomesStep = new PythonSdkDetailsStep(project, popupPoint.getComponent(), existingSdks, showMore, callback); final ListPopup popup = JBPopupFactory.getInstance().createListPopup(sdkHomesStep); popup.show(popupPoint); } - public InterpreterPathChooser(Project project, - Component ownerComponent, - Sdk[] existingSdks, - boolean showVirtualEnv, - NullableConsumer callback) { - super("Select Interpreter Path", getSuggestedPythonSdkPaths(existingSdks, showVirtualEnv)); + public PythonSdkDetailsStep(Project project, + Component ownerComponent, + Sdk[] existingSdks, + boolean showMore, + NullableConsumer callback) { + super(null, getAvailableOptions(showMore)); myProject = project; myOwnerComponent = ownerComponent; myExistingSdks = existingSdks; myCallback = callback; } - private static List getSuggestedPythonSdkPaths(Sdk[] existingSdks, boolean showVirtualEnv) { - List paths = new ArrayList(); - Collection sdkHomes = PythonSdkType.getInstance().suggestHomePaths(); - for (String sdkHome : SdkConfigurationUtil.filterExistingPaths(PythonSdkType.getInstance(), sdkHomes, existingSdks)) { - paths.add(FileUtil.getLocationRelativeToUserHome(sdkHome)); - } - paths.add(LOCAL); + private static List getAvailableOptions(boolean showMore) { + final List options = new ArrayList(); + options.add(LOCAL); if (PythonRemoteInterpreterManager.getInstance() != null) { - paths.add(REMOTE); + options.add(REMOTE); } - if (showVirtualEnv) { - paths.add(VIRTUALENV); + options.add(VIRTUALENV); + + if (showMore) { + options.add(MORE); } - return paths; + return options; } @Nullable @Override - public Icon getIconFor(String aValue) { - if (LOCAL.equals(aValue) || REMOTE.equals(aValue) || VIRTUALENV.equals(aValue)) return null; - String filePath = aValue; - if (StringUtil.startsWithChar(filePath, '~')) { - String home = SystemProperties.getUserHome(); - filePath = home + filePath.substring(1); - } - final PythonSdkFlavor flavor = PythonSdkFlavor.getPlatformIndependentFlavor(filePath); - return flavor != null ? flavor.getIcon() : PythonSdkType.getInstance().getIcon(); + public ListSeparator getSeparatorAbove(String value) { + return MORE.equals(value) ? new ListSeparator() : null; } - @NotNull - @Override - public String getTextFor(String value) { - return FileUtil.toSystemDependentName(value); - } - - private void sdkSelected(final String selectedValue) { + private void optionSelected(final String selectedValue) { if (LOCAL.equals(selectedValue)) { createLocalSdk(); } @@ -127,7 +105,7 @@ public class InterpreterPathChooser extends BaseListPopupStep { createVirtualEnvSdk(); } else { - createSdkFromPath(selectedValue); + myMore.show(); } } @@ -135,7 +113,7 @@ public class InterpreterPathChooser extends BaseListPopupStep { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - SdkConfigurationUtil.createSdk(myProject, myExistingSdks, myCallback, PythonSdkType.getInstance()); + SdkConfigurationUtil.createSdk(myProject, myExistingSdks, myCallback, false, PythonSdkType.getInstance()); } }, ModalityState.any()); } @@ -152,35 +130,43 @@ public class InterpreterPathChooser extends BaseListPopupStep { } } - private void createSdkFromPath(String selectedPath) { - String filePath = selectedPath; - if (StringUtil.startsWithChar(filePath, '~')) { - String home = SystemProperties.getUserHome(); - filePath = home + filePath.substring(1); - } - Sdk sdk = SdkConfigurationUtil.setupSdk(myExistingSdks, - LocalFileSystem.getInstance().findFileByPath(filePath), - PythonSdkType.getInstance(), false, null, null); - myCallback.consume(sdk); - } - private void createVirtualEnvSdk() { + CreateVirtualEnvDialog.VirtualEnvCallback callback = new CreateVirtualEnvDialog.VirtualEnvCallback() { + @Override + public void virtualEnvCreated(Sdk sdk, boolean associateWithProject) { + PythonSdkType.setupSdkPaths(sdk, myProject, null); + if (associateWithProject) { + SdkAdditionalData additionalData = sdk.getSdkAdditionalData(); + if (additionalData == null) { + additionalData = new PythonSdkAdditionalData(PythonSdkFlavor.getFlavor(sdk.getHomePath())); + ((ProjectJdkImpl)sdk).setSdkAdditionalData(additionalData); + } + ((PythonSdkAdditionalData)additionalData).associateWithProject(myProject); + } + myCallback.consume(sdk); + } + }; + final CreateVirtualEnvDialog dialog; - final List allSdks = Arrays.asList(myExistingSdks); + final List allSdks = Lists.newArrayList(myExistingSdks); + + final List flavors = PythonSdkFlavor.getApplicableFlavors(false); + for (PythonSdkFlavor flavor : flavors) { + final Collection strings = flavor.suggestHomePaths(); + for (String string : strings) { + allSdks.add(new PyDetectedSdk(string)); + } + } + if (myProject != null) { - dialog = new CreateVirtualEnvDialog(myProject, false, allSdks, null); + dialog = new CreateVirtualEnvDialog(myProject, allSdks, null); } else { - dialog = new CreateVirtualEnvDialog(myOwnerComponent, false, allSdks, null); + dialog = new CreateVirtualEnvDialog(myOwnerComponent, allSdks, null); } dialog.show(); if (dialog.isOK()) { - dialog.createVirtualEnv(allSdks, new CreateVirtualEnvDialog.VirtualEnvCallback() { - @Override - public void virtualEnvCreated(Sdk sdk, boolean associateWithProject, boolean setAsProjectInterpreter) { - myCallback.consume(sdk); - } - }); + dialog.createVirtualEnv(allSdks, callback); } } @@ -193,7 +179,7 @@ public class InterpreterPathChooser extends BaseListPopupStep { public PopupStep onChosen(final String selectedValue, boolean finalChoice) { return doFinalStep(new Runnable() { public void run() { - sdkSelected(selectedValue); + optionSelected(selectedValue); } }); } diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java index cb5f855e309f..3ad05e039612 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java @@ -45,7 +45,10 @@ import com.intellij.openapi.util.io.FileSystemUtil; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.CharFilter; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.*; +import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.reference.SoftReference; import com.intellij.remotesdk.RemoteSdkData; @@ -277,15 +280,16 @@ public class PythonSdkType extends SdkType { public void showCustomCreateUI(SdkModel sdkModel, final JComponent parentComponent, final Consumer sdkCreatedCallback) { Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(parentComponent)); - InterpreterPathChooser.show(project, sdkModel.getSdks(), RelativePoint.getCenterOf(parentComponent), true, new NullableConsumer() { - @Override - public void consume(@Nullable Sdk sdk) { - if (sdk != null) { - sdk.putUserData(SDK_CREATOR_COMPONENT_KEY, new WeakReference(parentComponent)); - sdkCreatedCallback.consume(sdk); + PythonSdkDetailsStep + .show(project, sdkModel.getSdks(), null, RelativePoint.getCenterOf(parentComponent), true, new NullableConsumer() { + @Override + public void consume(@Nullable Sdk sdk) { + if (sdk != null) { + sdk.putUserData(SDK_CREATOR_COMPONENT_KEY, new WeakReference(parentComponent)); + sdkCreatedCallback.consume(sdk); + } } - } - }); + }); } public static boolean isVirtualEnv(Sdk sdk) { diff --git a/python/src/com/jetbrains/python/sdk/flavors/MacPythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/flavors/MacPythonSdkFlavor.java index 780effe67dcb..5d241b668166 100644 --- a/python/src/com/jetbrains/python/sdk/flavors/MacPythonSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/flavors/MacPythonSdkFlavor.java @@ -16,6 +16,7 @@ package com.jetbrains.python.sdk.flavors; import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VFileProperty; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; @@ -57,7 +58,7 @@ public class MacPythonSdkFlavor extends CPythonSdkFlavor { if (binDir != null && binDir.isDirectory()) { for (String name : POSSIBLE_BINARY_NAMES) { final VirtualFile child = binDir.findChild(name); - if (child != null) { + if (child != null && !child.is(VFileProperty.SYMLINK)) { candidates.add(child.getPath()); break; } diff --git a/python/src/com/jetbrains/python/sdk/flavors/PythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/flavors/PythonSdkFlavor.java index 60fa8025a8a6..07c8e1c30c60 100644 --- a/python/src/com/jetbrains/python/sdk/flavors/PythonSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/flavors/PythonSdkFlavor.java @@ -72,6 +72,10 @@ public abstract class PythonSdkFlavor { } public static List getApplicableFlavors() { + return getApplicableFlavors(true); + } + + public static List getApplicableFlavors(boolean addPlatformIndependent) { List result = new ArrayList(); if (SystemInfo.isWindows) { @@ -84,7 +88,8 @@ public abstract class PythonSdkFlavor { result.add(UnixPythonSdkFlavor.INSTANCE); } - result.addAll(getPlatformIndependentFlavors()); + if (addPlatformIndependent) + result.addAll(getPlatformIndependentFlavors()); return result; } diff --git a/python/src/com/jetbrains/python/sdk/flavors/UnixPythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/flavors/UnixPythonSdkFlavor.java index c28587c36ecc..2440661877ff 100644 --- a/python/src/com/jetbrains/python/sdk/flavors/UnixPythonSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/flavors/UnixPythonSdkFlavor.java @@ -16,6 +16,7 @@ package com.jetbrains.python.sdk.flavors; import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VFileProperty; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; @@ -47,14 +48,15 @@ public class UnixPythonSdkFlavor extends CPythonSdkFlavor { if (rootDir instanceof NewVirtualFile) { ((NewVirtualFile)rootDir).markDirty(); } - rootDir.refresh(false, false); + rootDir.refresh(true, false); VirtualFile[] suspects = rootDir.getChildren(); for (VirtualFile child : suspects) { if (!child.isDirectory()) { final String childName = child.getName(); for (String name : NAMES) { if (childName.startsWith(name)) { - if (!childName.endsWith("-config") && !childName.startsWith("pythonw")) { + if (!childName.endsWith("-config") && !childName.startsWith("pythonw") && + !childName.endsWith("m") && !child.is(VFileProperty.SYMLINK)) { candidates.add(child.getPath()); } break; diff --git a/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java b/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java index 5bdecd7a521c..b9de2e2c9003 100644 --- a/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java @@ -83,7 +83,7 @@ public class VirtualEnvSdkFlavor extends CPythonSdkFlavor { public static Collection findInDirectory(VirtualFile rootDir) { List candidates = new ArrayList(); if (rootDir != null) { - rootDir.refresh(false, false); + rootDir.refresh(true, false); VirtualFile[] suspects = rootDir.getChildren(); for (VirtualFile child : suspects) { if (child.isDirectory()) { diff --git a/python/src/icons/PythonIcons.java b/python/src/icons/PythonIcons.java index c942e0ca02c6..a65d468beea9 100644 --- a/python/src/icons/PythonIcons.java +++ b/python/src/icons/PythonIcons.java @@ -32,6 +32,7 @@ public class PythonIcons { } public static final Icon Dotnet = load("/icons/com/jetbrains/python/dotnet.png"); // 16x16 + public static final Icon InterpreterGear = load("/icons/com/jetbrains/python/interpreterGear.png"); // 16x16 public static final Icon Jython = load("/icons/com/jetbrains/python/jython.png"); // 16x16 public static class Nodes { @@ -48,6 +49,7 @@ public class PythonIcons { public static final Icon Python_24 = load("/icons/com/jetbrains/python/python_24.png"); // 24x24 public static final Icon PythonClosed = load("/icons/com/jetbrains/python/pythonClosed.png"); // 16x16 public static final Icon PythonTests = load("/icons/com/jetbrains/python/pythonTests.png"); // 16x16 + public static final Icon Skeleton = load("/icons/com/jetbrains/python/skeleton.png"); // 16x16 public static final Icon Virtualenv = load("/icons/com/jetbrains/python/virtualenv.png"); // 16x16 }