package com.jetbrains.lang.dart.ide.runner.server.ui;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.ui.FixedSizeButton;
import com.intellij.openapi.ui.TextComponentAccessor;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.search.FileTypeIndex;
import com.intellij.psi.search.FilenameIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.GlobalSearchScopesCore;
import com.intellij.ui.*;
import com.intellij.util.PlatformIcons;
import com.jetbrains.lang.dart.DartFileType;
import com.jetbrains.lang.dart.ide.runner.server.DartRemoteDebugConfiguration;
import com.jetbrains.lang.dart.ide.runner.server.DartRemoteDebugParameters;
import com.jetbrains.lang.dart.sdk.DartSdk;
import com.jetbrains.lang.dart.util.PubspecYamlUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.util.SortedSet;
import java.util.TreeSet;
import static com.jetbrains.lang.dart.util.PubspecYamlUtil.PUBSPEC_YAML;
public class DartRemoteDebugConfigurationEditor extends SettingsEditor<DartRemoteDebugConfiguration> {
private JPanel myMainPanel;
private JTextArea myVMArgsArea;
private FixedSizeButton myCopyButton;
private JTextField myHostField;
private PortField myPortField;
private ComboboxWithBrowseButton myDartProjectCombo;
@Nullable private final DartSdk mySdk;
private SortedSet<NameAndPath> myComboItems = new TreeSet<>();
public DartRemoteDebugConfigurationEditor(@NotNull final Project project) {
mySdk = DartSdk.getDartSdk(project);
myHostField.getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
updateVmArgs();
}
});
myPortField.addChangeListener(e -> updateVmArgs());
initCopyToClipboardActions();
initDartProjectsCombo(project);
}
public void initCopyToClipboardActions() {
final DefaultActionGroup group = new DefaultActionGroup();
group.add(new AnAction("Copy") {
{
copyFrom(ActionManager.getInstance().getAction(IdeActions.ACTION_COPY));
}
@Override
public void actionPerformed(final AnActionEvent e) {
CopyPasteManager.getInstance().setContents(new StringSelection(myVMArgsArea.getText().trim()));
}
});
myVMArgsArea.addMouseListener(
new PopupHandler() {
@Override
public void invokePopup(final Component comp, final int x, final int y) {
ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, group).getComponent().show(comp, x, y);
}
}
);
myCopyButton.setSize(22);
myCopyButton.setIcon(PlatformIcons.COPY_ICON);
myCopyButton.addActionListener(e -> CopyPasteManager.getInstance().setContents(new StringSelection(myVMArgsArea.getText().trim())));
}
private void initDartProjectsCombo(@NotNull final Project project) {
myDartProjectCombo.getComboBox().setRenderer(new ListCellRendererWrapper<NameAndPath>() {
@Override
public void customize(final JList list,
final NameAndPath value,
final int index,
final boolean selected,
final boolean hasFocus) {
if (value != null) {
setText(value.getPresentableText());
}
}
});
for (VirtualFile pubspecFile : FilenameIndex.getVirtualFilesByName(project, PUBSPEC_YAML, GlobalSearchScope.projectScope(project))) {
myComboItems.add(new NameAndPath(PubspecYamlUtil.getDartProjectName(pubspecFile), pubspecFile.getParent().getPath()));
}
if (myComboItems.isEmpty()) {
for (VirtualFile contentRoot : ProjectRootManager.getInstance(project).getContentRoots()) {
if (FileTypeIndex.containsFileOfType(DartFileType.INSTANCE, GlobalSearchScopesCore.directoryScope(project, contentRoot, true))) {
myComboItems.add(new NameAndPath(null, contentRoot.getPath()));
}
}
}
myDartProjectCombo.getComboBox().setModel(new DefaultComboBoxModel(myComboItems.toArray()));
myDartProjectCombo.addBrowseFolderListener(null, null, project, FileChooserDescriptorFactory.createSingleFolderDescriptor(),
new TextComponentAccessor<JComboBox>() {
@Override
public String getText(final JComboBox combo) {
final Object item = combo.getSelectedItem();
return item instanceof NameAndPath ? ((NameAndPath)item).myPath : "";
}
@Override
public void setText(final JComboBox combo, @NotNull final String path) {
setSelectedProjectPath(FileUtil.toSystemIndependentName(path));
}
});
}
@NotNull
@Override
protected JComponent createEditor() {
return myMainPanel;
}
private void updateVmArgs() {
final String host = myHostField.getText().trim();
final boolean localhost = "localhost".equals(host) || "127.0.0.1".equals(host);
myVMArgsArea.setText("--enable-vm-service:" + myPortField.getNumber() + (localhost ? "" : "/0.0.0.0") + " --pause_isolates_on_start");
}
@Override
protected void resetEditorFrom(@NotNull final DartRemoteDebugConfiguration config) {
final DartRemoteDebugParameters params = config.getParameters();
myHostField.setText(params.getHost());
myPortField.setNumber(params.getPort());
setSelectedProjectPath(params.getDartProjectPath());
updateVmArgs();
}
private void setSelectedProjectPath(@NotNull final String projectPath) {
if (projectPath.isEmpty()) return;
final VirtualFile pubspecFile = LocalFileSystem.getInstance().findFileByPath(projectPath + "/" + PUBSPEC_YAML);
final String projectName = pubspecFile == null ? null : PubspecYamlUtil.getDartProjectName(pubspecFile);
final NameAndPath item = new NameAndPath(projectName, projectPath);
if (!myComboItems.contains(item)) {
myComboItems.add(item);
myDartProjectCombo.getComboBox().setModel(new DefaultComboBoxModel(myComboItems.toArray()));
}
myDartProjectCombo.getComboBox().setSelectedItem(item);
}
@Override
protected void applyEditorTo(@NotNull final DartRemoteDebugConfiguration config) throws ConfigurationException {
final DartRemoteDebugParameters params = config.getParameters();
params.setHost(myHostField.getText().trim());
params.setPort(myPortField.getNumber());
final Object selectedItem = myDartProjectCombo.getComboBox().getSelectedItem();
params.setDartProjectPath(selectedItem instanceof NameAndPath ? ((NameAndPath)selectedItem).myPath : "");
}
private static class NameAndPath implements Comparable<NameAndPath> {
@Nullable private final String myName;
@NotNull private final String myPath;
public NameAndPath(@Nullable final String name, @NotNull final String path) {
myName = name;
myPath = path;
}
public String getPresentableText() {
return myName == null ? FileUtil.toSystemDependentName(myPath) : myName + " (" + FileUtil.toSystemDependentName(myPath) + ")";
}
@Override
public String toString() {
return getPresentableText();
}
@Override
public boolean equals(final Object o) {
return (o instanceof NameAndPath) && myPath.equals(((NameAndPath)o).myPath);
}
@Override
public int hashCode() {
return myPath.hashCode();
}
@Override
public int compareTo(final NameAndPath o) {
return myPath.compareTo(o.myPath); // root project goes first, before its subprojects
}
}
}