PY-22830 Improve UI for remote notebooks

This commit is contained in:
Valentina Kiryushkina
2017-03-07 00:08:43 +03:00
parent 8f25b4a200
commit ec3dbce697
6 changed files with 449 additions and 155 deletions
+1
View File
@@ -26,5 +26,6 @@
</orderEntry>
<orderEntry type="library" name="markdown4j-2.2" level="project" />
<orderEntry type="library" name="http-client" level="project" />
<orderEntry type="module" module-name="python-community-configure" />
</component>
</module>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.plugins.ipnb.configuration.IpnbConfigurable">
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="5" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="657" height="302"/>
@@ -8,7 +8,7 @@
<properties/>
<border type="none"/>
<children>
<grid id="6ed7f" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="6ed7f" layout-manager="GridLayoutManager" row-count="2" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
@@ -21,18 +21,18 @@
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Jupyter Notebook URL:"/>
<text value="&amp;URL:"/>
</properties>
</component>
<component id="5a5b" class="com.intellij.ui.components.JBTextField" binding="myFieldUrl">
<component id="5a5b" class="com.intellij.ui.components.JBTextField" binding="myUrlField">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="24f12" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myWorkingDirField">
<component id="24f12" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myNotebookDirectoryField">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
<grid row="1" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
@@ -42,103 +42,36 @@
</constraints>
<properties>
<labelFor value="24f12"/>
<text value="&amp;Working directory:"/>
<text value="&amp;Notebook directory:"/>
</properties>
</component>
<component id="97534" class="com.intellij.ui.components.JBTextField" binding="myArgumentsField">
<component id="8a71c" class="com.intellij.ui.components.JBLabel" binding="myWarningIcon">
<constraints>
<grid row="2" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false">
<minimum-size width="18" height="18"/>
</grid>
</constraints>
<properties/>
</component>
<component id="83e21" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Arguments:"/>
</properties>
</component>
</children>
</grid>
<grid id="18b6b" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="18b6b" binding="mySpecificNotebookSettingsPanel" custom-create="true" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="true" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="2" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
<grid row="1" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none">
<font/>
<color color="-4539718"/>
</border>
<children>
<component id="85ddd" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="39ad9"/>
<text value="&amp;Password:"/>
</properties>
</component>
<component id="5fd16" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="&amp;Username:"/>
</properties>
</component>
<component id="39ad9" class="javax.swing.JPasswordField" binding="myPasswordField">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="150" height="-1"/>
</grid>
</constraints>
<properties/>
</component>
<component id="a47cd" class="com.intellij.ui.components.JBTextField" binding="myUsernameField">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
</children>
<children/>
</grid>
<vspacer id="9a11e">
<constraints>
<grid row="4" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
<grid row="2" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="b88d4" class="com.intellij.ui.TitledSeparator">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Remote notebook credentials"/>
</properties>
</component>
<grid id="161aa" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="9a652" class="com.intellij.ui.components.JBLabel" binding="myWarningLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<hspacer id="cab57">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
</children>
</grid>
</children>
</grid>
</form>
@@ -1,44 +1,56 @@
package org.jetbrains.plugins.ipnb.configuration;
import com.intellij.icons.AllIcons;
import com.intellij.ide.DataManager;
import com.intellij.ide.IdeTooltipManager;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.options.ex.Settings;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.ui.VerticalFlowLayout;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.JBColor;
import com.intellij.ui.ClickListener;
import com.intellij.ui.TooltipWithClickableLinks;
import com.intellij.ui.UI;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBPasswordField;
import com.intellij.ui.components.JBRadioButton;
import com.intellij.ui.components.JBTextField;
import com.intellij.util.PlatformUtils;
import com.intellij.util.ui.UIUtil;
import com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable;
import net.miginfocom.swing.MigLayout;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.*;
public class IpnbConfigurable implements SearchableConfigurable {
private static final int DEFAULT_PADDING = 3;
private JPanel myMainPanel;
private JBTextField myFieldUrl;
private TextFieldWithBrowseButton myWorkingDirField;
private JBTextField myArgumentsField;
private JPasswordField myPasswordField;
private JBTextField myUsernameField;
private JBLabel myWarningLabel;
@NotNull private final Project myProject;
private JBTextField myUrlField;
private TextFieldWithBrowseButton myNotebookDirectoryField;
private JBLabel myWarningIcon;
private JPanel mySpecificNotebookSettingsPanel;
private final Project myProject;
public IpnbConfigurable(@NotNull Project project) {
myProject = project;
myWarningLabel.setForeground(new JBColor(new Color(164, 145, 52), new Color(187, 181, 41)));
final FileChooserDescriptor fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
myWorkingDirField.addBrowseFolderListener("Select Working Directory", null, myProject, fileChooserDescriptor);
myFieldUrl.setText(IpnbSettings.getInstance(myProject).getURL());
myFieldUrl.addFocusListener(createUrlProtocolValidationListener());
myUsernameField.addFocusListener(createUrlProtocolValidationListener());
myPasswordField.addFocusListener(createUrlProtocolValidationListener());
myWorkingDirField.setText(IpnbSettings.getInstance(myProject).getWorkingDirectory());
myArgumentsField.setText(IpnbSettings.getInstance(myProject).getArguments());
myNotebookDirectoryField.addBrowseFolderListener("Select Working Directory", null, myProject, fileChooserDescriptor);
myUrlField.setText(IpnbSettings.getInstance(myProject).getURL());
myUrlField.addFocusListener(createUrlProtocolValidationListener());
final String workingDirectory = IpnbSettings.getInstance(myProject).getWorkingDirectory();
myNotebookDirectoryField.setText(workingDirectory.isEmpty() ? myProject.getBasePath() : workingDirectory);
}
@NotNull
@@ -46,24 +58,27 @@ public class IpnbConfigurable implements SearchableConfigurable {
return new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
setWarningLabelText();
setWarningLabelIcon(IpnbSettings.getInstance(myProject).isRemote());
}
};
}
private void setWarningLabelText() {
final boolean isRemote = myPasswordField.getPassword().length > 0;
final String url = myFieldUrl.getText();
if (isRemote && !url.startsWith("https")) {
myWarningLabel.setText("Only HTTPS urls are supported in remote notebooks");
return;
}
if (!isRemote && url.startsWith("https")) {
myWarningLabel.setText("Only HTTP urls are supported in local notebooks");
private void setWarningLabelIcon(boolean isRemote) {
if (myUrlField == null) return;
final String url = myUrlField.getText();
if (isRemote && !url.startsWith("https") || !isRemote && url.startsWith("https") ) {
Dimension oldPreferredSize = myWarningIcon.getPreferredSize();
myWarningIcon.setIcon(AllIcons.General.BalloonWarning);
myWarningIcon.setPreferredSize(oldPreferredSize);
myWarningIcon.setMinimumSize(oldPreferredSize);
myWarningIcon.revalidate();
String message = isRemote ? "Use HTTPS for remote notebooks" : "Use HTTP for local notebooks";
IdeTooltipManager.getInstance().setCustomTooltip(myWarningIcon, new TooltipWithClickableLinks.ForBrowser(myWarningIcon, message));
return;
}
myWarningLabel.setText("");
myWarningIcon.setIcon(AllIcons.Nodes.EmptyNode);
}
@Nls
@@ -86,55 +101,39 @@ public class IpnbConfigurable implements SearchableConfigurable {
public boolean isModified() {
final String oldUrl = IpnbSettings.getInstance(myProject).getURL();
final String oldWorkingDirectory = IpnbSettings.getInstance(myProject).getWorkingDirectory();
final String oldArguments = StringUtil.notNullize(IpnbSettings.getInstance(myProject).getArguments());
final String url = StringUtil.trimEnd(StringUtil.notNullize(myFieldUrl.getText()), "/");
final String workingDirectory = StringUtil.notNullize(myWorkingDirField.getText());
final String arguments = StringUtil.notNullize(myArgumentsField.getText());
final String url = StringUtil.trimEnd(StringUtil.notNullize(myUrlField.getText()), "/");
final String workingDirectory = StringUtil.notNullize(myNotebookDirectoryField.getText());
return !url.equals(oldUrl) || !workingDirectory.equals(oldWorkingDirectory) || !arguments.equals(oldArguments)
|| isCredentialsModified();
}
public boolean isCredentialsModified() {
final String oldUsername = StringUtil.notNullize(IpnbSettings.getInstance(myProject).getUsername());
final String oldPassword = IpnbSettings.getInstance(myProject).getPassword(myProject.getLocationHash());
final String username = StringUtil.notNullize(myUsernameField.getText());
final String password = StringUtil.notNullize(String.valueOf(myPasswordField.getPassword()));
return !oldUsername.equals(username) || !oldPassword.equals(password);
final boolean isBaseModified = !url.equals(oldUrl) || !workingDirectory.equals(oldWorkingDirectory);
return isBaseModified || ((SettingsPanel)mySpecificNotebookSettingsPanel).isModified();
}
@Override
public void apply() throws ConfigurationException {
IpnbSettings.getInstance(myProject).setWorkingDirectory(myWorkingDirField.getText());
IpnbSettings.getInstance(myProject).setArguments(myArgumentsField.getText());
final IpnbSettings ipnbSettings = IpnbSettings.getInstance(myProject);
final String newNotebookDir = myNotebookDirectoryField.getText();
ipnbSettings.setWorkingDirectory(newNotebookDir.isEmpty() ? myProject.getBasePath() : newNotebookDir);
String url = StringUtil.notNullize(myFieldUrl.getText());
url = StringUtil.trimEnd(url, "/");
final boolean urlModified = !url.equals(IpnbSettings.getInstance(myProject).getURL());
if (urlModified) {
String url = StringUtil.trimEnd(StringUtil.notNullize(myUrlField.getText()), "/");
if (!url.equals(ipnbSettings.getURL())) {
IpnbConnectionManager.getInstance(myProject).shutdownKernels();
IpnbSettings.getInstance(myProject).setURL(url);
}
if (isCredentialsModified()) {
IpnbConnectionManager.getInstance(myProject).shutdownKernels();
IpnbSettings.getInstance(myProject).setUsername(myUsernameField.getText());
IpnbSettings.getInstance(myProject).setPassword(String.valueOf(myPasswordField.getPassword()), myProject.getLocationHash());
ipnbSettings.setURL(url);
}
((SettingsPanel)mySpecificNotebookSettingsPanel).apply();
}
@Override
public void reset() {
myFieldUrl.setText(IpnbSettings.getInstance(myProject).getURL());
myWorkingDirField.setText(IpnbSettings.getInstance(myProject).getWorkingDirectory());
myArgumentsField.setText(IpnbSettings.getInstance(myProject).getArguments());
myUsernameField.setText(IpnbSettings.getInstance(myProject).getUsername());
myPasswordField.setText(IpnbSettings.getInstance(myProject).getPassword(myProject.getLocationHash()));
myUrlField.setText(IpnbSettings.getInstance(myProject).getURL());
final String workingDirectory = IpnbSettings.getInstance(myProject).getWorkingDirectory();
myNotebookDirectoryField.setText(workingDirectory.isEmpty() ? myProject.getBasePath() : workingDirectory);
((SettingsPanel)mySpecificNotebookSettingsPanel).reset();
setWarningLabelText();
setWarningLabelIcon(IpnbSettings.getInstance(myProject).isRemote());
}
@NotNull
@@ -142,5 +141,362 @@ public class IpnbConfigurable implements SearchableConfigurable {
public String getId() {
return "IpnbConfigurable";
}
private void createUIComponents() {
// TODO: remove this hack
int gap = SystemInfo.isWindows ? 1 : 2;
mySpecificNotebookSettingsPanel = PlatformUtils.isPyCharmPro() ? new SettingsPanelPro() : new LocalSettingsPanel(true, 0, gap);
}
private abstract static class SettingsPanel extends JPanel {
public abstract boolean isModified();
public abstract void apply();
public abstract void reset();
}
private class SettingsPanelPro extends SettingsPanel {
private final JBRadioButton myLocal;
private final JBRadioButton myRemote;
private final RemoteSettingsPanel myRemoteSettingsPanel;
private final LocalSettingsPanel myLocalSettingsPanel;
public SettingsPanelPro() {
final boolean isRemote = IpnbSettings.getInstance(myProject).isRemote();
myLocal = createModeRadioButton("Local");
myLocal.addItemListener(createRadioButtonListener(myLocal));
myRemote = createModeRadioButton("Remote");
myRemoteSettingsPanel = new RemoteSettingsPanel(isRemote);
myLocalSettingsPanel = new LocalSettingsPanel(!isRemote, DEFAULT_PADDING, 1);
final ButtonGroup group = new ButtonGroup();
myLocal.setSelected(!isRemote);
myRemote.setSelected(isRemote);
group.add(myLocal);
group.add(myRemote);
final JPanel localPanel = new JPanel(new BorderLayout(10, 1));
localPanel.add(myLocal, BorderLayout.NORTH);
localPanel.add(myLocalSettingsPanel, BorderLayout.CENTER);
final JPanel remotePanel = new JPanel(new BorderLayout(10, 1));
remotePanel.add(myRemote, BorderLayout.NORTH);
remotePanel.add(myRemoteSettingsPanel, BorderLayout.CENTER);
setLayout(new VerticalFlowLayout(VerticalFlowLayout.MIDDLE, 0, 1, true, false));
add(localPanel);
add(remotePanel);
setWarningLabelIcon(isRemote);
}
public boolean isModified() {
final boolean wasRemote = IpnbSettings.getInstance(myProject).isRemote();
final boolean isRemote = myRemote.isSelected();
if (wasRemote != isRemote) return true;
if (wasRemote) {
return myRemoteSettingsPanel.isModified();
}
else {
return myLocalSettingsPanel.isModified();
}
}
@Override
public void apply() {
final boolean isRemote = myRemote.isSelected();
final boolean wasRemote = IpnbSettings.getInstance(myProject).isRemote();
if (isRemote != wasRemote) {
IpnbConnectionManager.getInstance(myProject).shutdownKernels();
IpnbSettings.getInstance(myProject).setRemote(isRemote);
}
if (isRemote) {
myRemoteSettingsPanel.apply();
}
else {
myLocalSettingsPanel.apply();
}
}
@Override
public void reset() {
final boolean isRemote = IpnbSettings.getInstance(myProject).isRemote();
myRemote.setSelected(isRemote);
myLocal.setSelected(!isRemote);
if (isRemote) {
myRemoteSettingsPanel.reset();
}
else {
myLocalSettingsPanel.reset();
}
}
@NotNull
private ItemListener createRadioButtonListener(@NotNull JBRadioButton local) {
return new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (local.isSelected()) {
myLocalSettingsPanel.enablePanel();
myRemoteSettingsPanel.disablePanel();
setWarningLabelIcon(false);
}
else {
myLocalSettingsPanel.disablePanel();
myRemoteSettingsPanel.enablePanel();
setWarningLabelIcon(true);
}
}
};
}
}
@NotNull
private static JBRadioButton createModeRadioButton(@NotNull String text) {
final JBRadioButton local = new JBRadioButton(text);
local.setFocusable(false);
local.setBorder(null);
return local;
}
private class LocalSettingsPanel extends SettingsPanel {
private static final String PARAMETERS_TEXT = "Additional options:";
private static final String DEFAULT_PARAMETERS_TEXT = "for example: --profile=nbserver";
private JBTextField myAdditionalOptions;
private final JLabel myParametersLabel;
public LocalSettingsPanel(boolean enabled, int leftPadding, int gap) {
myParametersLabel = new JLabel(PARAMETERS_TEXT);
myAdditionalOptions = new JBTextField();
myAdditionalOptions.addFocusListener(createInitialTextFocusAdapter(myAdditionalOptions, DEFAULT_PARAMETERS_TEXT));
initFields();
setLayout(new MigLayout("insets 0", leftPadding + "unrel[]" + gap + "unrel[grow]", "[][]"));
add(myParametersLabel);
add(myAdditionalOptions, "growx");
if (enabled) {
enablePanel();
}
else {
disablePanel();
}
}
public void enablePanel() {
myParametersLabel.setEnabled(true);
myParametersLabel.setForeground(UIUtil.getActiveTextColor());
myAdditionalOptions.setEnabled(true);
}
public void disablePanel() {
myParametersLabel.setEnabled(false);
myParametersLabel.setForeground(UIUtil.getInactiveTextColor());
myAdditionalOptions.setEnabled(false);
}
@Override
public void apply() {
final String parameters = myAdditionalOptions.getText();
if (!parameters.equals(DEFAULT_PARAMETERS_TEXT)) {
IpnbSettings.getInstance(myProject).setArguments(parameters);
}
else {
IpnbSettings.getInstance(myProject).setArguments("");
}
}
@Override
public void reset() {
initFields();
}
private void initFields() {
final String arguments = IpnbSettings.getInstance(myProject).getArguments();
setInitialText(myAdditionalOptions, arguments, DEFAULT_PARAMETERS_TEXT);
}
public boolean isModified() {
final IpnbSettings ipnbSettings = IpnbSettings.getInstance(myProject);
final String arguments = ipnbSettings.getArguments();
final String text = StringUtil.trim(StringUtil.notNullize(myAdditionalOptions.getText()));
return !text.equals(arguments);
}
}
private class RemoteSettingsPanel extends SettingsPanel {
private static final String DEFAULT_USERNAME_TEXT = "Leave empty for a single-user notebook";
private JBTextField myUsernameField;
private JBPasswordField myPasswordField;
private JLabel myInterpreterSetupLinkLabel;
private JBLabel myPasswordLabel;
private JBLabel myUsernameLabel;
private boolean myIsEnabled;
public RemoteSettingsPanel(boolean enabled) {
myIsEnabled = enabled;
myInterpreterSetupLinkLabel = new JLabel("Configure remote interpreter");
myInterpreterSetupLinkLabel.setForeground(UI.getColor("link.foreground"));
myInterpreterSetupLinkLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
createNavigateToInterpreterSettingsListener().installOn(myInterpreterSetupLinkLabel);
setLayout(new MigLayout("insets 0", DEFAULT_PADDING + "unrel[]5unrel[grow]", "[][][]"));
myUsernameLabel = new JBLabel("Username:");
myUsernameField = new JBTextField();
myUsernameField.addFocusListener(createInitialTextFocusAdapter(myUsernameField, DEFAULT_USERNAME_TEXT));
setInitialText(myUsernameField, IpnbSettings.getInstance(myProject).getUsername(), DEFAULT_USERNAME_TEXT);
myPasswordLabel = new JBLabel("Password:");
myPasswordField = new JBPasswordField();
add(myUsernameLabel);
add(myUsernameField, "cell 1 0, growx, wrap");
add(myPasswordLabel);
add(myPasswordField, "cell 1 1, growx, wrap");
add(myInterpreterSetupLinkLabel, "cell 1 2, align right");
if (myIsEnabled) {
enablePanel();
}
else {
disablePanel();
}
}
@NotNull
private ClickListener createNavigateToInterpreterSettingsListener() {
return new ClickListener() {
@Override
public boolean onClick(@NotNull MouseEvent event, int clickCount) {
if (myIsEnabled) {
final Settings settings = Settings.KEY.getData(DataManager.getInstance().getDataContext(RemoteSettingsPanel.this));
if (settings != null) {
settings.select(settings.find(PyActiveSdkModuleConfigurable.class.getName()));
return true;
}
}
return false;
}
};
}
public void enablePanel() {
myIsEnabled = true;
myUsernameField.setEnabled(true);
myUsernameLabel.setEnabled(true);
myUsernameLabel.setForeground(UIUtil.getActiveTextColor());
if (DEFAULT_USERNAME_TEXT.equals(myUsernameField.getText())) {
myUsernameField.setForeground(UIUtil.getInactiveTextColor());
}
myPasswordField.setEnabled(true);
myPasswordLabel.setEnabled(true);
myPasswordLabel.setForeground(UIUtil.getActiveTextColor());
myInterpreterSetupLinkLabel.setEnabled(true);
myInterpreterSetupLinkLabel.setForeground(UI.getColor("link.foreground"));
myInterpreterSetupLinkLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
}
public void disablePanel() {
myIsEnabled = false;
myUsernameField.setEnabled(false);
myUsernameLabel.setEnabled(false);
myUsernameLabel.setForeground(UIUtil.getInactiveTextColor());
myPasswordField.setEnabled(false);
myPasswordLabel.setEnabled(false);
myPasswordLabel.setForeground(UIUtil.getInactiveTextColor());
myInterpreterSetupLinkLabel.setEnabled(false);
myInterpreterSetupLinkLabel.setForeground(UIUtil.getInactiveTextColor());
myInterpreterSetupLinkLabel.setCursor(Cursor.getDefaultCursor());
}
@Override
public void apply() {
final IpnbSettings ipnbSettings = IpnbSettings.getInstance(myProject);
final String oldUsername = ipnbSettings.getUsername();
final String oldPassword = ipnbSettings.getPassword(myProject.getLocationHash());
final String newUsername = myUsernameField.getText().equals(DEFAULT_USERNAME_TEXT) ? "" : myUsernameField.getText();
final String newPassword = String.valueOf(myPasswordField.getPassword());
if (!oldUsername.equals(newUsername) || !oldPassword.equals(newPassword)) {
IpnbConnectionManager.getInstance(myProject).shutdownKernels();
ipnbSettings.setUsername(newUsername);
ipnbSettings.setPassword(newPassword, myProject.getLocationHash());
}
}
@Override
public void reset() {
final IpnbSettings ipnbSettings = IpnbSettings.getInstance(myProject);
final String savedUsername = ipnbSettings.getUsername();
setInitialText(myUsernameField, savedUsername, DEFAULT_USERNAME_TEXT);
final String savedPassword = ipnbSettings.getPassword(myProject.getLocationHash());
myPasswordField.setText(savedPassword);
}
public boolean isModified() {
final IpnbSettings ipnbSettings = IpnbSettings.getInstance(myProject);
final String oldUsername = ipnbSettings.getUsername();
final String oldPassword = ipnbSettings.getPassword(myProject.getLocationHash());
final String newPassword = String.valueOf(myPasswordField.getPassword());
final String newUsername = myUsernameField.getText();
return !oldPassword.equals(newPassword) || !oldUsername.equals(newUsername);
}
}
@NotNull
private static FocusAdapter createInitialTextFocusAdapter(@NotNull JBTextField field, @NotNull String initialText) {
return new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
if (field.getText().equals(initialText)) {
field.setForeground(UIUtil.getActiveTextColor());
field.setText("");
}
}
@Override
public void focusLost(FocusEvent e) {
if (field.getText().isEmpty()) {
field.setForeground(UIUtil.getInactiveTextColor());
field.setText(initialText);
}
}
};
}
private static void setInitialText(@NotNull JBTextField field,
@NotNull String savedValue,
@NotNull String defaultText) {
if (savedValue.isEmpty()) {
field.setForeground(UIUtil.getInactiveTextColor());
field.setText(defaultText);
}
else {
field.setForeground(UIUtil.getActiveTextColor());
field.setText(savedValue);
}
}
}
@@ -105,7 +105,7 @@ public final class IpnbConnectionManager implements ProjectComponent {
@NotNull final String path) {
final String url = getURL();
if (connectToIpythonServer(codePanel, fileEditor, path, url)) return;
final boolean isRemote = IpnbSettings.getInstance(myProject).isRemote(myProject.getLocationHash());
final boolean isRemote = IpnbSettings.getInstance(myProject).isRemote();
if (!isRemote) {
ApplicationManager.getApplication().executeOnPooledThread(() -> {
final boolean serverStarted = startIpythonServer(url, fileEditor);
@@ -122,7 +122,7 @@ public final class IpnbConnectionManager implements ProjectComponent {
@NotNull final String path,
@NotNull final String url) {
final IpnbSettings ipnbSettings = IpnbSettings.getInstance(myProject);
final boolean isRemote = ipnbSettings.isRemote(myProject.getLocationHash());
final boolean isRemote = ipnbSettings.isRemote();
if (!isRemote) {
if (myToken != null) return startConnection(codePanel, path, url, true);
final Module module = ProjectFileIndex.SERVICE.getInstance(myProject).getModuleForFile(fileEditor.getVirtualFile());
@@ -19,6 +19,15 @@ public class IpnbSettings implements PersistentStateComponent<IpnbSettings> {
private String myWorkingDirectory;
private String myArguments = "";
private String myUsername;
private boolean myIsRemote;
public boolean isRemote() {
return myIsRemote;
}
public void setRemote(boolean remote) {
myIsRemote = remote;
}
public static IpnbSettings getInstance(@NotNull Project project) {
return ServiceManager.getService(project, IpnbSettings.class);
@@ -77,11 +86,6 @@ public class IpnbSettings implements PersistentStateComponent<IpnbSettings> {
final String accountName = createAccountName(username, url, projectPathHash);
PasswordSafe.getInstance().setPassword(IpnbSettings.class, accountName, password);
}
public boolean isRemote(String locationHash) {
final String password = getPassword(locationHash);
return !password.isEmpty();
}
private static String createAccountName(@NotNull String username, @NotNull String url, @NotNull String projectPath) {
return IPNB_PASSWORD_KEY + url + username + projectPath;
@@ -94,7 +94,7 @@ public class IpnbConnection {
myProject = project;
myCookieManager = new CookieManager();
CookieHandler.setDefault(myCookieManager);
if (!IpnbSettings.getInstance(project).isRemote(project.getLocationHash())) {
if (!IpnbSettings.getInstance(project).isRemote()) {
if (!"http".equals(myURI.getScheme())) {
throw new UnsupportedOperationException("Only http urls are supported for local notebooks");
}
@@ -103,7 +103,7 @@ public class IpnbConnection {
throw new UnsupportedOperationException("Only https urls are supported for remote notebooks");
}
if (IpnbSettings.getInstance(project).isRemote(project.getLocationHash())) {
if (IpnbSettings.getInstance(project).isRemote()) {
String loginUrl = getLoginUrl();
initXSRF(myURI.toString() + loginUrl);
myIsHubServer = isHubServer(loginUrl);