package com.intellij.javascript.karma.execution;
import com.intellij.execution.configuration.EnvironmentVariablesTextFieldWithBrowseButton;
import com.intellij.javascript.karma.KarmaBundle;
import com.intellij.javascript.karma.scope.KarmaScopeKind;
import com.intellij.javascript.karma.scope.KarmaScopeView;
import com.intellij.javascript.karma.util.KarmaUtil;
import com.intellij.javascript.nodejs.interpreter.NodeJsInterpreterField;
import com.intellij.javascript.nodejs.util.NodePackageField;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.options.ex.SingleConfigurableEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.TextFieldWithHistory;
import com.intellij.ui.TextFieldWithHistoryWithBrowseButton;
import com.intellij.ui.components.JBTextField;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.*;
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.Collections;
import java.util.List;
import java.util.Map;
public class KarmaRunConfigurationEditor extends SettingsEditor<KarmaRunConfiguration> {
private final Project myProject;
private final NodeJsInterpreterField myNodeInterpreterField;
private final NodePackageField myKarmaPackageField;
private final TextFieldWithHistoryWithBrowseButton myConfigPathField;
private final EnvironmentVariablesTextFieldWithBrowseButton myEnvVarsComponent;
private final JTextField myBrowsers;
private final Map<KarmaScopeKind, JRadioButton> myRadioButtonMap = ContainerUtil.newHashMap();
private final Map<KarmaScopeKind, KarmaScopeView> myScopeKindViewMap = ContainerUtil.newHashMap();
private final JPanel mySelectedScopeKindPanel;
private final JPanel myRootComponent;
private final int myLongestLabelWidth = new JLabel("Environment variables:").getPreferredSize().width;
public KarmaRunConfigurationEditor(@NotNull Project project) {
myProject = project;
myNodeInterpreterField = new NodeJsInterpreterField(project, false);
myKarmaPackageField = new NodePackageField(project, KarmaUtil.NODE_PACKAGE_NAME, myNodeInterpreterField::getInterpreter);
myConfigPathField = createConfigurationFileTextField(project);
myEnvVarsComponent = new EnvironmentVariablesTextFieldWithBrowseButton();
myBrowsers = createBrowsersTextField();
JComponent browsersDescription = createBrowsersDescription();
JPanel scopeKindPanel = createScopeKindRadioButtonPanel();
mySelectedScopeKindPanel = new JPanel(new BorderLayout());
myRootComponent = new FormBuilder()
.setAlignLabelOnRight(false)
.addLabeledComponent(KarmaBundle.message("runConfiguration.config_file.label"), myConfigPathField)
.addLabeledComponent(KarmaBundle.message("runConfiguration.browsers.label"), myBrowsers)
.addLabeledComponent("", browsersDescription, 0, false)
.addComponent(new JSeparator(), 8)
.addLabeledComponent(KarmaBundle.message("runConfiguration.node_interpreter.label"), myNodeInterpreterField, 8)
.addLabeledComponent(KarmaBundle.message("runConfiguration.karma_package_dir.label"), myKarmaPackageField)
.addLabeledComponent(KarmaBundle.message("runConfiguration.environment.label"), myEnvVarsComponent)
.addSeparator(8)
.addComponent(scopeKindPanel)
.addComponent(mySelectedScopeKindPanel)
.getPanel();
}
@NotNull
private static JComponent createBrowsersDescription() {
Color fgColor = UIUtil.getLabelDisabledForeground();
JEditorPane editorPane = SwingHelper.createHtmlViewer(true, UIUtil.getTitledBorderFont(), null, fgColor);
SwingHelper.setHtml(editorPane, "overrides <i>browsers</i> setting from the configuration file", fgColor);
JPanel panel = SwingHelper.wrapWithHorizontalStretch(editorPane);
panel.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 0));
return panel;
}
@NotNull
private JPanel createScopeKindRadioButtonPanel() {
JPanel testKindPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, JBUI.scale(40), 0));
testKindPanel.setBorder(IdeBorderFactory.createEmptyBorder(0, 10, 0, 0));
ButtonGroup buttonGroup = new ButtonGroup();
for (KarmaScopeKind scopeKind : KarmaScopeKind.values()) {
JRadioButton radioButton = new JRadioButton(UIUtil.removeMnemonic(scopeKind.getName()));
final int index = UIUtil.getDisplayMnemonicIndex(scopeKind.getName());
if (index != -1) {
radioButton.setMnemonic(scopeKind.getName().charAt(index + 1));
radioButton.setDisplayedMnemonicIndex(index);
}
radioButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setScopeKind(scopeKind);
}
});
myRadioButtonMap.put(scopeKind, radioButton);
testKindPanel.add(radioButton);
buttonGroup.add(radioButton);
}
return testKindPanel;
}
private void setScopeKind(@NotNull KarmaScopeKind scopeKind) {
KarmaScopeKind selectedScopeKind = getScopeKind();
if (selectedScopeKind != scopeKind) {
JRadioButton radioButton = myRadioButtonMap.get(scopeKind);
radioButton.setSelected(true);
}
KarmaScopeView view = getScopeKindView(scopeKind);
setCenterBorderLayoutComponent(mySelectedScopeKindPanel, view.getComponent());
}
@Nullable
private KarmaScopeKind getScopeKind() {
for (Map.Entry<KarmaScopeKind, JRadioButton> entry : myRadioButtonMap.entrySet()) {
if (entry.getValue().isSelected()) {
return entry.getKey();
}
}
return null;
}
@NotNull
private KarmaScopeView getScopeKindView(@NotNull KarmaScopeKind scopeKind) {
KarmaScopeView view = myScopeKindViewMap.get(scopeKind);
if (view == null) {
view = scopeKind.createView(myProject);
myScopeKindViewMap.put(scopeKind, view);
JComponent component = view.getComponent();
if (component.getLayout() instanceof GridBagLayout) {
component.add(Box.createHorizontalStrut(myLongestLabelWidth), new GridBagConstraints(
0, GridBagConstraints.RELATIVE,
1, 1,
0.0, 0.0,
GridBagConstraints.EAST,
GridBagConstraints.NONE,
JBUI.insetsRight(UIUtil.DEFAULT_HGAP),
0, 0
));
}
}
return view;
}
private static void setCenterBorderLayoutComponent(@NotNull JPanel panel, @NotNull Component child) {
Component prevChild = ((BorderLayout)panel.getLayout()).getLayoutComponent(BorderLayout.CENTER);
if (prevChild != null) {
panel.remove(prevChild);
}
panel.add(child, BorderLayout.CENTER);
panel.revalidate();
panel.repaint();
}
@NotNull
private static JTextField createBrowsersTextField() {
JBTextField browsers = new JBTextField();
StatusText emptyStatusText = browsers.getEmptyText();
emptyStatusText.setText("comma-separated list of browsers (e.g. Chrome,ChromeCanary,Firefox)");
return browsers;
}
@NotNull
private static TextFieldWithHistoryWithBrowseButton createConfigurationFileTextField(@NotNull final Project project) {
TextFieldWithHistoryWithBrowseButton textFieldWithHistoryWithBrowseButton = new TextFieldWithHistoryWithBrowseButton();
final TextFieldWithHistory textFieldWithHistory = textFieldWithHistoryWithBrowseButton.getChildComponent();
textFieldWithHistory.setHistorySize(-1);
textFieldWithHistory.setMinimumAndPreferredWidth(0);
SwingHelper.addHistoryOnExpansion(textFieldWithHistory, () -> {
List<VirtualFile> newFiles = KarmaUtil.listPossibleConfigFilesInProject(project);
List<String> newFilePaths = ContainerUtil.map(newFiles, file -> FileUtil.toSystemDependentName(file.getPath()));
Collections.sort(newFilePaths);
return newFilePaths;
});
SwingHelper.installFileCompletionAndBrowseDialog(
project,
textFieldWithHistoryWithBrowseButton,
KarmaBundle.message("runConfiguration.config_file.browse_dialog.title"),
FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor()
);
return textFieldWithHistoryWithBrowseButton;
}
@Override
protected void resetEditorFrom(@NotNull KarmaRunConfiguration runConfiguration) {
KarmaRunSettings runSettings = runConfiguration.getRunSettings();
myNodeInterpreterField.setInterpreterRef(runSettings.getInterpreterRef());
myKarmaPackageField.setSelected(runConfiguration.getKarmaPackage());
myConfigPathField.setTextAndAddToHistory(FileUtil.toSystemDependentName(runSettings.getConfigPath()));
myBrowsers.setText(runSettings.getBrowsers());
myEnvVarsComponent.setData(runSettings.getEnvData());
setScopeKind(runSettings.getScopeKind());
KarmaScopeView view = getScopeKindView(runSettings.getScopeKind());
view.resetFrom(runSettings);
updatePreferredWidth();
}
private void updatePreferredWidth() {
DialogWrapper dialogWrapper = DialogWrapper.findInstance(myRootComponent);
if (dialogWrapper instanceof SingleConfigurableEditor) {
// dialog for single run configuration
myNodeInterpreterField.setPreferredWidthToFitText();
myKarmaPackageField.setPreferredWidthToFitText();
SwingHelper.setPreferredWidthToFitText(myConfigPathField);
ApplicationManager.getApplication().invokeLater(() -> SwingHelper.adjustDialogSizeToFitPreferredSize(dialogWrapper), ModalityState.any());
}
}
@Override
protected void applyEditorTo(@NotNull KarmaRunConfiguration runConfiguration) throws ConfigurationException {
KarmaRunSettings.Builder builder = new KarmaRunSettings.Builder();
builder.setConfigPath(myConfigPathField.getChildComponent().getText());
builder.setBrowsers(StringUtil.notNullize(myBrowsers.getText()));
builder.setInterpreterRef(myNodeInterpreterField.getInterpreterRef());
builder.setEnvData(myEnvVarsComponent.getData());
builder.setKarmaPackage(myKarmaPackageField.getSelected());
KarmaScopeKind scopeKind = getScopeKind();
if (scopeKind != null) {
builder.setScopeKind(scopeKind);
KarmaScopeView view = getScopeKindView(scopeKind);
view.applyTo(builder);
}
runConfiguration.setRunSettings(builder.build());
}
@NotNull
@Override
protected JComponent createEditor() {
return myRootComponent;
}
}