package org.dlangplugin.run;
import com.intellij.application.options.ModulesComboBox;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.ConfigurationType;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.options.SettingsEditorListener;
import com.intellij.openapi.ui.TextComponentAccessor;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.ui.RawCommandLineEditor;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import org.apache.commons.lang.StringUtils;
import org.dlangplugin.DLangBundle;
import org.dlangplugin.module.DLangModuleType;
import org.dlangplugin.run.exception.NoSourcesException;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
/** DMD Compiler arguments UI. All properties are read from official docs:
* @see http://dlang.org/dmd-linux.html#switches
*/
public class DLangRunDmdConfigurationEditor extends SettingsEditor<DLangRunDmdConfiguration> {
private JTabbedPane myMainPanel;
private JPanel tabCompiler;
private JPanel tabOutput;
private JPanel tabDebug;
private JPanel tabArguments;
//"Compile" tab
private ModulesComboBox comboModules;
private JCheckBox cbRelease;
private JCheckBox cbDebug;
private JCheckBox cbUnitTest;
private JCheckBox cbLink;
private JCheckBox cbCoverageAnalysis;
private JCheckBox cbAllowDeprecated;
private JCheckBox cbIgnorePragmas;
private JCheckBox cbFunctionInlining;
private JCheckBox cbLibrary;
private JCheckBox cbNoArrayBoundsCheck;
private JCheckBox cbNoFloatingPointReferences;
private JCheckBox cbOptimize;
private JCheckBox cbEnforcePropertySyntax;
private JCheckBox cbQuiet;
private JCheckBox cbVerbose;
private JCheckBox cbListThreadLocalStorage;
private JCheckBox cbWarnings;
private JCheckBox cbInfoWarnings;
private JTextField textDefaultLibrary;
private TextFieldWithBrowseButton pathImports;
private TextFieldWithBrowseButton pathStringImports;
private RawCommandLineEditor linkerArgs;
//"Output" tab
private JCheckBox cbGenerateDocumentation;
private JTextField filenameDocumentation;
private TextFieldWithBrowseButton pathDocumentation;
private JTextField filenameModuleDependencies;
private JCheckBox cbGenerateHeader;
private TextFieldWithBrowseButton pathHeaderDir;
private JTextField filenameHeader;
private JCheckBox cbGenerateMap;
private JCheckBox cbNoObjectFiles;
private JCheckBox cbNoStripPaths;
private JCheckBox cbGenerateJson;
private JTextField filenameJson;
//"Debug" tab
private JCheckBox cbAddSymbolicDebugInfo;
private JCheckBox cbAddSymbolicDebugInfoC;
private JCheckBox cbGenerateStandardStackFrame;
private JTextField textSymbolicLibrary;
private JCheckBox cbProfile;
//"Arguments" tab
private JLabel linkDmdDoc;
private JTextPane textArgsPane;
private JBScrollPane textArgsScrollPane;
/** Update editor UI with data of DLangRunDmdConfiguration.
* All components must be changed according to "config" data. */
@Override
protected void resetEditorFrom(DLangRunDmdConfiguration config) {
resetCompilerTabForm(config);
resetOutputTabForm(config);
resetDebugTabForm(config);
fillArguments(config);
}
/** Save state of editor UI to DLangRunDmdConfiguration instance. */
@Override
protected void applyEditorTo(DLangRunDmdConfiguration config) throws ConfigurationException {
applyCompilerTabForm(config);
applyOutputTabForm(config);
applyDebugTabForm(config);
}
@NotNull
@Override
protected JComponent createEditor() {
FileChooserDescriptor fcd = FileChooserDescriptorFactory.createSingleFolderDescriptor();
fcd.setShowFileSystemRoots(true);
fcd.setTitle(DLangBundle.message("dmd.run.config.selectimportfolder.title"));
fcd.setDescription(DLangBundle.message("dmd.run.config.selectimportfolder.description"));
fcd.setHideIgnored(false);
pathImports.addBrowseFolderListener(null,
new TextFieldWithBrowseButton.BrowseFolderActionListener<JTextField>(fcd.getTitle(), fcd.getDescription(),
pathImports, null, fcd, TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT) );
//XXX: fix title and description
pathStringImports.addBrowseFolderListener(null,
new TextFieldWithBrowseButton.BrowseFolderActionListener<JTextField>(fcd.getTitle(), fcd.getDescription(),
pathStringImports, null, fcd, TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT) );
//XXX: fix title and description
pathDocumentation.addBrowseFolderListener(null,
new TextFieldWithBrowseButton.BrowseFolderActionListener<JTextField>(fcd.getTitle(), fcd.getDescription(),
pathDocumentation, null, fcd, TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT) );
//XXX: fix title and description
pathHeaderDir.addBrowseFolderListener(null,
new TextFieldWithBrowseButton.BrowseFolderActionListener<JTextField>(fcd.getTitle(), fcd.getDescription(),
pathHeaderDir, null, fcd, TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT) );
cbGenerateDocumentation.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
final boolean enabled = cbGenerateDocumentation.isSelected();
filenameDocumentation.setEnabled(enabled);
pathDocumentation.setEnabled(enabled);
}
});
cbGenerateHeader.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
final boolean enabled = cbGenerateHeader.isSelected();
pathHeaderDir.setEnabled(enabled);
filenameHeader.setEnabled(enabled);
}
});
cbGenerateJson.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
filenameJson.setEnabled(cbGenerateJson.isSelected());
}
});
setDocLink();
/* Each time settings are changed "Arguments" tab must be updated.
* Add listener here. */
final DLangRunDmdConfigurationType configurationType
= Extensions.findExtension(ConfigurationType.CONFIGURATION_TYPE_EP, DLangRunDmdConfigurationType.class);
final ConfigurationFactory factory = configurationType.getConfigurationFactories()[0];
addSettingsEditorListener(new SettingsEditorListener<DLangRunDmdConfiguration>() {
@Override
public void stateChanged(SettingsEditor<DLangRunDmdConfiguration> editor) {
try {
Module module = comboModules.getSelectedModule();
if(module!=null) {
DLangRunDmdConfiguration config = (DLangRunDmdConfiguration) factory.createTemplateConfiguration(module.getProject());
applyEditorTo(config); //Save current editor state to "config"
fillArguments(config); //Convert "config" to DMD arguments to display on "Arguments" tab.
}
}
catch (Exception e) {
//pass
}
}
});
//wrap "textArgsPane" with JBScrollPane to allow scrolling.
Container tabContent = textArgsPane.getParent();
GridLayoutManager layout = (GridLayoutManager)tabContent.getLayout();
GridConstraints constraints = layout.getConstraintsForComponent(textArgsPane);
textArgsScrollPane = new JBScrollPane(textArgsPane);
tabContent.add(textArgsScrollPane, constraints);
tabContent.remove(textArgsPane);
return myMainPanel;
}
@Override
protected void disposeEditor() {
}
/* I hope there is no misprints in next methods :) */
private void resetCompilerTabForm(DLangRunDmdConfiguration config) {
comboModules.fillModules(config.getProject(), DLangModuleType.getInstance());
comboModules.setSelectedModule(config.getConfigurationModule().getModule());
cbRelease.setSelected(config.isRelease());
cbDebug.setSelected(config.isDebug());
cbUnitTest.setSelected(config.isUnitTest());
cbLink.setSelected(config.isLink());
cbCoverageAnalysis.setSelected(config.isCoverageAnalysis());
cbAllowDeprecated.setSelected(config.isAllowDeprecated());
cbIgnorePragmas.setSelected(config.isIgnorePragmas());
cbFunctionInlining.setSelected(config.isFunctionInlining());
cbLibrary.setSelected(config.isLibrary());
cbNoArrayBoundsCheck.setSelected(config.isNoArrayBoundsCheck());
cbNoFloatingPointReferences.setSelected(config.isNoFloatingPointReferences());
cbOptimize.setSelected(config.isOptimize());
cbEnforcePropertySyntax.setSelected(config.isEnforcePropertySyntax());
cbQuiet.setSelected(config.isQuiet());
cbVerbose.setSelected(config.isVerbose());
cbListThreadLocalStorage.setSelected(config.isListThreadLocalStorage());
cbWarnings.setSelected(config.isWarnings());
cbInfoWarnings.setSelected(config.isInfoWarnings());
textDefaultLibrary.setText(config.getDefaultLibrary());
pathImports.setText(config.getImportsPath());
pathStringImports.setText(config.getStringImportsPath());
linkerArgs.setText(config.getLinkerArgs());
}
private void resetOutputTabForm(DLangRunDmdConfiguration config) {
cbGenerateDocumentation.setSelected(config.isGenerateDocs());
filenameDocumentation.setText(config.getDocsFilename());
pathDocumentation.setText(config.getDocsPath());
filenameModuleDependencies.setText(config.getModuleDepsFilename());
cbGenerateHeader.setSelected(config.isGenerateHeader());
pathHeaderDir.setText(config.getHeaderDir());
filenameHeader.setText(config.getHeaderFilename());
cbGenerateMap.setSelected(config.isGenerateMap());
cbNoObjectFiles.setSelected(config.isNoObjectFiles());
cbNoStripPaths.setSelected(config.isNoStripPaths());
cbGenerateJson.setSelected(config.isGenerateJson());
filenameJson.setText(config.getJsonFilename());
}
private void resetDebugTabForm(DLangRunDmdConfiguration config) {
cbAddSymbolicDebugInfo.setSelected(config.isAddSymbolicDebugInfo());
cbAddSymbolicDebugInfoC.setSelected(config.isAddSymbolicDebugInfoC());
cbGenerateStandardStackFrame.setSelected(config.isGenerateStandardStackFrame());
textSymbolicLibrary.setText(config.getSymbolicLibrary());
cbProfile.setSelected(config.isProfile());
}
/* Make "linkDmdDoc" clickable. */
private void setDocLink() {
linkDmdDoc.setText("<html><a href=\"http://dlang.org/dmd-linux.html#switches\">Documentation at dlang.org</a></html>");
linkDmdDoc.setCursor(new Cursor(Cursor.HAND_CURSOR));
linkDmdDoc.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() > 0) {
if (Desktop.isDesktopSupported()) {
Desktop desktop = Desktop.getDesktop();
try {
URI uri = new URI("http://dlang.org/dmd-linux.html#switches");
desktop.browse(uri);
} catch (IOException ex) {
//do nothing
} catch (URISyntaxException ex) {
//do nothing
}
}
}
}
});
}
private void applyCompilerTabForm(DLangRunDmdConfiguration config) {
config.setModule(comboModules.getSelectedModule());
config.setRelease(cbRelease.isSelected());
config.setDebug(cbDebug.isSelected());
config.setUnitTest(cbUnitTest.isSelected());
config.setLink(cbLink.isSelected());
config.setCoverageAnalysis(cbCoverageAnalysis.isSelected());
config.setAllowDeprecated(cbAllowDeprecated.isSelected());
config.setIgnorePragmas(cbIgnorePragmas.isSelected());
config.setFunctionInlining(cbFunctionInlining.isSelected());
config.setLibrary(cbLibrary.isSelected());
config.setNoArrayBoundsCheck(cbNoArrayBoundsCheck.isSelected());
config.setNoFloatingPointReferences(cbNoFloatingPointReferences.isSelected());
config.setOptimize(cbOptimize.isSelected());
config.setEnforcePropertySyntax(cbEnforcePropertySyntax.isSelected());
config.setQuiet(cbQuiet.isSelected());
config.setVerbose(cbVerbose.isSelected());
config.setListThreadLocalStorage(cbListThreadLocalStorage.isSelected());
config.setWarnings(cbWarnings.isSelected());
config.setInfoWarnings(cbInfoWarnings.isSelected());
config.setDefaultLibrary(textDefaultLibrary.getText());
config.setImportsPath(pathImports.getText());
config.setStringImportsPath(pathStringImports.getText());
config.setLinkerArgs(linkerArgs.getText());
}
private void applyOutputTabForm(DLangRunDmdConfiguration config) {
config.setGenerateDocs(cbGenerateDocumentation.isSelected());
config.setDocsFilename(filenameDocumentation.getText());
config.setDocsPath(pathDocumentation.getText());
config.setModuleDepsFilename(filenameModuleDependencies.getText());
config.setGenerateHeader(cbGenerateHeader.isSelected());
config.setHeaderDir(pathHeaderDir.getText());
config.setHeaderFilename(filenameHeader.getText());
config.setGenerateMap(cbGenerateMap.isSelected());
config.setNoObjectFiles(cbNoObjectFiles.isSelected());
config.setNoStripPaths(cbNoStripPaths.isSelected());
config.setGenerateJson(cbGenerateJson.isSelected());
config.setJsonFilename(filenameJson.getText());
}
private void applyDebugTabForm(DLangRunDmdConfiguration config) {
config.setAddSymbolicDebugInfo(cbAddSymbolicDebugInfo.isSelected());
config.setAddSymbolicDebugInfoC(cbAddSymbolicDebugInfoC.isSelected());
config.setGenerateStandardStackFrame(cbGenerateStandardStackFrame.isSelected());
config.setSymbolicLibrary(textSymbolicLibrary.getText());
config.setProfile(cbProfile.isSelected());
}
/** Update "textArgsPane" text area with actual DMD command line arguments */
private void fillArguments(DLangRunDmdConfiguration config) {
Module module = comboModules.getSelectedModule();
if(module == null) {
return;
}
try {
java.util.List<String> args = DLangDmdConfigToArgsConverter.getDmdParameters(config, module);
textArgsPane.setText(StringUtils.join(args, "\n"));
} catch (NoSourcesException e) {
textArgsPane.setText("*Exception*:\n" + e.getMessage());
} catch (ExecutionException e) {
textArgsPane.setText("*Exception*:\n" + e.getMessage());
}
}
}