package com.jetbrains.lang.dart.projectWizard;
import com.intellij.icons.AllIcons;
import com.intellij.ide.browsers.BrowserSpecificSettings;
import com.intellij.ide.browsers.WebBrowser;
import com.intellij.ide.browsers.chrome.ChromeSettings;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.ide.util.projectWizard.SettingsStep;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.ValidationInfo;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.platform.WebProjectGenerator;
import com.intellij.ui.ColorUtil;
import com.intellij.ui.ComboboxWithBrowseButton;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBList;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.util.ui.AsyncProcessIcon;
import com.intellij.xml.util.XmlStringUtil;
import com.jetbrains.lang.dart.DartBundle;
import com.jetbrains.lang.dart.ide.runner.client.DartiumUtil;
import com.jetbrains.lang.dart.sdk.DartSdkUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.util.List;
public class DartGeneratorPeer implements WebProjectGenerator.GeneratorPeer<DartProjectWizardData> {
private static final String DART_PROJECT_TEMPLATE = "DART_PROJECT_TEMPLATE";
private static final String CREATE_SAMPLE_UNCHECKED = "CREATE_SAMPLE_UNCHECKED";
private JPanel myMainPanel;
private ComboboxWithBrowseButton mySdkPathComboWithBrowse;
private JBLabel myVersionLabel;
private JPanel myDartiumSettingsPanel;
private ComboboxWithBrowseButton myDartiumPathComboWithBrowse;
private JButton myDartiumSettingsButton;
private JBCheckBox myCheckedModeCheckBox;
private JPanel myTemplatesPanel;
private JPanel myLoadingTemplatesPanel;
private JPanel myLoadedTemplatesPanel;
private JBCheckBox myCreateSampleProjectCheckBox;
private JBList myTemplatesList;
private JBLabel myErrorLabel; // shown in IntelliJ IDEA only
private ChromeSettings myDartiumSettingsCurrent;
private boolean myIntellijLiveValidationEnabled = false;
public DartGeneratorPeer() {
// set initial values before initDartSdkAndDartiumControls() because listeners should not be triggered on initialization
mySdkPathComboWithBrowse.getComboBox().setEditable(true);
//mySdkPathComboWithBrowse.getComboBox().getEditor().setItem(...); initial sdk path will be correctly taken from known paths history
final WebBrowser dartiumInitial = DartiumUtil.getDartiumBrowser();
myDartiumSettingsCurrent = new ChromeSettings();
if (dartiumInitial != null) {
final BrowserSpecificSettings browserSpecificSettings = dartiumInitial.getSpecificSettings();
if (browserSpecificSettings instanceof ChromeSettings) {
myDartiumSettingsCurrent = (ChromeSettings)browserSpecificSettings.clone();
}
}
final String dartiumPath = dartiumInitial == null ? ""
: FileUtilRt.toSystemDependentName(StringUtil.notNullize(dartiumInitial.getPath()));
myDartiumPathComboWithBrowse.getComboBox().setEditable(true);
myDartiumPathComboWithBrowse.getComboBox().getEditor().setItem(dartiumPath);
// now setup controls
DartSdkUtil.initDartSdkAndDartiumControls(null, mySdkPathComboWithBrowse, myVersionLabel, myDartiumPathComboWithBrowse,
new Computable.PredefinedValueComputable<>(myDartiumSettingsCurrent),
myDartiumSettingsButton,
new Computable.PredefinedValueComputable<>(false));
// we decided to save one line in settings and always use Dartium in checked mode
DartiumUtil.setCheckedMode(myDartiumSettingsCurrent.getEnvironmentVariables(), true);
myCheckedModeCheckBox.setVisible(false);
//final boolean checkedMode = dartiumInitial == null || DartiumUtil.isCheckedMode(myDartiumSettingsCurrent.getEnvironmentVariables());
//myCheckedModeCheckBox.setSelected(checkedMode);
myCreateSampleProjectCheckBox.addActionListener(e -> myTemplatesList.setEnabled(myCreateSampleProjectCheckBox.isSelected()));
myTemplatesList.setEmptyText(DartBundle.message("set.sdk.to.see.sample.content.options"));
myErrorLabel.setIcon(AllIcons.Actions.Lightning);
myErrorLabel.setVisible(false);
final String sdkPath = mySdkPathComboWithBrowse.getComboBox().getEditor().getItem().toString().trim();
final String message = DartSdkUtil.getErrorMessageIfWrongSdkRootPath(sdkPath);
if (message == null) {
startLoadingTemplates();
}
else {
myLoadingTemplatesPanel.setVisible(false);
myCreateSampleProjectCheckBox.setEnabled(false);
myTemplatesList.setEnabled(false);
final JTextComponent editorComponent = (JTextComponent)mySdkPathComboWithBrowse.getComboBox().getEditor().getEditorComponent();
editorComponent.getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(final DocumentEvent e) {
final String sdkPath = mySdkPathComboWithBrowse.getComboBox().getEditor().getItem().toString().trim();
final String message = DartSdkUtil.getErrorMessageIfWrongSdkRootPath(sdkPath);
if (message == null) {
editorComponent.getDocument().removeDocumentListener(this);
startLoadingTemplates();
}
}
});
}
}
private void startLoadingTemplates() {
myLoadingTemplatesPanel.setVisible(true);
myLoadingTemplatesPanel.setPreferredSize(myLoadedTemplatesPanel.getPreferredSize());
myLoadedTemplatesPanel.setVisible(false);
myCreateSampleProjectCheckBox.setSelected(false); // until loaded
final AsyncProcessIcon asyncProcessIcon = new AsyncProcessIcon("Dart project templates loading");
myLoadingTemplatesPanel.add(asyncProcessIcon, new GridConstraints()); // defaults are ok: row = 0, column = 0
asyncProcessIcon.resume();
ApplicationManager.getApplication().executeOnPooledThread(() -> {
final String sdkPath = mySdkPathComboWithBrowse.getComboBox().getEditor().getItem().toString().trim();
DartProjectTemplate.loadTemplatesAsync(sdkPath, templates -> {
asyncProcessIcon.suspend();
Disposer.dispose(asyncProcessIcon);
onTemplatesLoaded(templates);
});
});
}
private void onTemplatesLoaded(final List<DartProjectTemplate> templates) {
myLoadingTemplatesPanel.setVisible(false);
myLoadedTemplatesPanel.setVisible(true);
myCreateSampleProjectCheckBox.setEnabled(true);
final String selectedTemplateName = PropertiesComponent.getInstance().getValue(DART_PROJECT_TEMPLATE);
myCreateSampleProjectCheckBox.setSelected(!CREATE_SAMPLE_UNCHECKED.equals(selectedTemplateName));
myTemplatesList.setVisibleRowCount(Math.min(8, templates.size()));
myTemplatesList.setEnabled(myCreateSampleProjectCheckBox.isSelected());
DartProjectTemplate selectedTemplate = null;
final DefaultListModel model = new DefaultListModel();
for (DartProjectTemplate template : templates) {
model.addElement(template);
if (template.getName().equals(selectedTemplateName)) {
selectedTemplate = template;
}
}
myTemplatesList.setModel(model);
if (selectedTemplate != null) {
myTemplatesList.setSelectedValue(selectedTemplate, true);
}
else if (templates.size() > 0) {
myTemplatesList.setSelectedIndex(0);
}
myTemplatesList.setCellRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
final JLabel component = (JLabel)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
final DartProjectTemplate template = (DartProjectTemplate)value;
final String text = template.getDescription().isEmpty()
? template.getName()
: template.getName() + " - " + StringUtil.decapitalize(template.getDescription());
component.setText(text);
return component;
}
});
}
@NotNull
@Override
public JComponent getComponent() {
return myMainPanel;
}
@Override
public void buildUI(final @NotNull SettingsStep settingsStep) {
settingsStep.addSettingsField(DartBundle.message("dart.sdk.path.label"), mySdkPathComboWithBrowse);
settingsStep.addSettingsField(DartBundle.message("version.label"), myVersionLabel);
settingsStep.addSettingsField(DartBundle.message("dartium.path.label"), myDartiumSettingsPanel);
//settingsStep.addSettingsField("", myCheckedModeCheckBox); we decided to save one line in settings and always use Dartium in checked mode
settingsStep.addSettingsComponent(myTemplatesPanel);
}
@NotNull
@Override
public DartProjectWizardData getSettings() {
final String sdkPath = FileUtil.toSystemIndependentName(mySdkPathComboWithBrowse.getComboBox().getEditor().getItem().toString().trim());
final String dartiumPath =
FileUtil.toSystemIndependentName(myDartiumPathComboWithBrowse.getComboBox().getEditor().getItem().toString().trim());
final DartProjectTemplate template = myCreateSampleProjectCheckBox.isSelected()
? (DartProjectTemplate)myTemplatesList.getSelectedValue() : null;
PropertiesComponent.getInstance().setValue(DART_PROJECT_TEMPLATE, template == null ? CREATE_SAMPLE_UNCHECKED : template.getName());
return new DartProjectWizardData(sdkPath, dartiumPath, myDartiumSettingsCurrent, template);
}
@Nullable
@Override
public ValidationInfo validate() {
// invalid Dartium path is not a blocking error
final String sdkPath = mySdkPathComboWithBrowse.getComboBox().getEditor().getItem().toString().trim();
final String message = DartSdkUtil.getErrorMessageIfWrongSdkRootPath(sdkPath);
if (message != null) {
return new ValidationInfo(message, mySdkPathComboWithBrowse);
}
if (myCreateSampleProjectCheckBox.isSelected()) {
if (myTemplatesList.getSelectedValue() == null) {
return new ValidationInfo(DartBundle.message("project.template.not.selected"), myCreateSampleProjectCheckBox);
}
}
return null;
}
public boolean validateInIntelliJ() {
final ValidationInfo info = validate();
if (info == null) {
myErrorLabel.setVisible(false);
return true;
}
else {
myErrorLabel.setVisible(true);
myErrorLabel
.setText(XmlStringUtil.wrapInHtml("<font color='#" + ColorUtil.toHex(JBColor.RED) + "'><left>" + info.message + "</left></font>"));
if (!myIntellijLiveValidationEnabled) {
myIntellijLiveValidationEnabled = true;
enableIntellijLiveValidation();
}
return false;
}
}
private void enableIntellijLiveValidation() {
final JTextComponent editorComponent = (JTextComponent)mySdkPathComboWithBrowse.getComboBox().getEditor().getEditorComponent();
editorComponent.getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(final DocumentEvent e) {
validateInIntelliJ();
}
});
myCreateSampleProjectCheckBox.addActionListener(e -> validateInIntelliJ());
myTemplatesList.addListSelectionListener(e -> validateInIntelliJ());
}
@Override
public boolean isBackgroundJobRunning() {
return false;
}
@Override
public void addSettingsStateListener(final @NotNull WebProjectGenerator.SettingsStateListener stateListener) {
// invalid Dartium path is not a blocking error
final JTextComponent editorComponent = (JTextComponent)mySdkPathComboWithBrowse.getComboBox().getEditor().getEditorComponent();
editorComponent.getDocument().addDocumentListener(new DocumentAdapter() {
protected void textChanged(final DocumentEvent e) {
stateListener.stateChanged(validate() == null);
}
});
myCreateSampleProjectCheckBox.addActionListener(e -> stateListener.stateChanged(validate() == null));
myTemplatesList.addListSelectionListener(e -> stateListener.stateChanged(validate() == null));
}
private void createUIComponents() {
mySdkPathComboWithBrowse = new ComboboxWithBrowseButton(new ComboBox<>());
myDartiumPathComboWithBrowse = new ComboboxWithBrowseButton(new ComboBox<>());
}
}