package org.infernus.idea.checkstyle.ui; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.io.IOException; import java.util.Map; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JPanel; import javax.swing.border.EmptyBorder; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.wm.WindowManager; import com.intellij.util.ui.JBUI; import org.infernus.idea.checkstyle.CheckStyleBundle; import org.infernus.idea.checkstyle.checker.CheckerFactory; import org.infernus.idea.checkstyle.checker.CheckerFactoryCache; import org.infernus.idea.checkstyle.model.ConfigurationLocation; import org.jetbrains.annotations.NotNull; /** * Allows selection of the location of the CheckStyle file. */ public class LocationDialogue extends JDialog { private static final Insets COMPONENT_INSETS = JBUI.insets(4); private static final int WIDTH = 500; private static final int HEIGHT = 400; private enum Step { SELECT(false, true, false), PROPERTIES(true, true, false), ERROR(true, false, false), COMPLETE(true, false, true); private final boolean allowPrevious; private final boolean allowNext; private final boolean allowCommit; Step(final boolean allowPrevious, final boolean allowNext, final boolean allowCommit) { this.allowPrevious = allowPrevious; this.allowNext = allowNext; this.allowCommit = allowCommit; } private boolean isAllowPrevious() { return allowPrevious; } private boolean isAllowNext() { return allowNext; } private boolean isAllowCommit() { return allowCommit; } } private final Project project; private final LocationPanel locationPanel; private final PropertiesPanel propertiesPanel; private final ErrorPanel errorPanel; private final CompletePanel completePanel; private JButton commitButton; private JButton previousButton; private Step currentStep = Step.SELECT; private boolean committed = true; private ConfigurationLocation configurationLocation; public LocationDialogue(@NotNull final Project project) { super(WindowManager.getInstance().getFrame(project)); this.project = project; this.locationPanel = new LocationPanel(project); this.propertiesPanel = new PropertiesPanel(project); this.errorPanel = new ErrorPanel(); this.completePanel = new CompletePanel(); initialise(); } public void initialise() { setLayout(new BorderLayout()); setMinimumSize(new Dimension(WIDTH, HEIGHT)); setModal(true); commitButton = new JButton(new NextAction()); previousButton = new JButton(new PreviousAction()); final JButton cancelButton = new JButton(new CancelAction()); final JPanel bottomPanel = new JPanel(new GridBagLayout()); bottomPanel.setBorder(new EmptyBorder(4, 8, 8, 8)); if (SystemInfo.isMac) { bottomPanel.add(cancelButton, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, COMPONENT_INSETS, 0, 0)); bottomPanel.add(Box.createHorizontalGlue(), new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, COMPONENT_INSETS, 0, 0)); } else { bottomPanel.add(Box.createHorizontalGlue(), new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, COMPONENT_INSETS, 0, 0)); bottomPanel.add(cancelButton, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, COMPONENT_INSETS, 0, 0)); } bottomPanel.add(previousButton, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, COMPONENT_INSETS, 0, 0)); bottomPanel.add(commitButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, COMPONENT_INSETS, 0, 0)); add(panelForCurrentStep(), BorderLayout.CENTER); add(bottomPanel, BorderLayout.SOUTH); getRootPane().setDefaultButton(commitButton); moveToStep(Step.SELECT); pack(); final Toolkit toolkit = Toolkit.getDefaultToolkit(); setLocation((toolkit.getScreenSize().width - getSize().width) / 2, (toolkit.getScreenSize().height - getSize().height) / 2); } private JPanel panelForCurrentStep() { switch (currentStep) { case SELECT: return locationPanel; case PROPERTIES: return propertiesPanel; case ERROR: return errorPanel; case COMPLETE: return completePanel; default: throw new IllegalStateException("Unknown step: " + currentStep); } } @Override public void setVisible(final boolean visible) { if (visible) { this.committed = false; } super.setVisible(visible); } /** * Get the configuration location entered in the dialogue, or null if no valid location was entered. * * @return the location or null if no valid location entered. */ public ConfigurationLocation getConfigurationLocation() { return configurationLocation; } private void moveToStep(final Step newStep) { remove(panelForCurrentStep()); currentStep = newStep; if (currentStep.isAllowCommit()) { commitButton.setText(CheckStyleBundle.message("config.file.okay.text")); commitButton.setToolTipText(CheckStyleBundle.message("config.file.okay.text")); } else { commitButton.setText(CheckStyleBundle.message("config.file.next.text")); commitButton.setToolTipText(CheckStyleBundle.message("config.file.next.text")); } previousButton.setEnabled(currentStep.isAllowPrevious()); commitButton.setEnabled(currentStep.isAllowNext() || currentStep.isAllowCommit()); getContentPane().add(panelForCurrentStep(), BorderLayout.CENTER); getContentPane().validate(); getContentPane().repaint(); } /** * Was okay clicked? * * @return true if okay clicked, false if cancelled. */ public boolean isCommitted() { return committed; } private Step attemptLoadOfFile(final ConfigurationLocation location) { configurationLocation = location; final CheckerFactoryCache cache = new CheckerFactoryCache(); try { location.reset(); new CheckerFactory(project, cache).verify(location); return Step.COMPLETE; } catch (Exception e) { errorPanel.setError(e); return Step.ERROR; } } private class NextAction extends AbstractAction { private static final long serialVersionUID = 3800521701284308642L; @Override public void actionPerformed(final ActionEvent event) { commitButton.setEnabled(false); final ConfigurationLocation location; switch (currentStep) { case SELECT: location = locationPanel.getConfigurationLocation(); if (location == null) { showError(CheckStyleBundle.message("config.file.no-file")); return; } if (location.getDescription() == null || location.getDescription().isEmpty()) { showError(CheckStyleBundle.message("config.file.no-description")); return; } Map<String, String> properties; try { location.resolve(); properties = location.getProperties(); } catch (IOException e) { showError(CheckStyleBundle.message("config.file.resolve-failed", e.getMessage())); return; } if (properties == null || properties.isEmpty()) { moveToStep(attemptLoadOfFile(location)); } else { propertiesPanel.setConfigurationLocation(location); moveToStep(Step.PROPERTIES); } return; case PROPERTIES: location = propertiesPanel.getConfigurationLocation(); moveToStep(attemptLoadOfFile(location)); return; case COMPLETE: committed = true; setVisible(false); return; default: throw new IllegalStateException("Unexpected next call for step " + currentStep); } } } private void showError(final String formattedMessage) { Messages.showErrorDialog(this, formattedMessage, CheckStyleBundle.message("config.file.error.title")); commitButton.setEnabled(true); } private class PreviousAction extends AbstractAction { private static final long serialVersionUID = 3800521701284308642L; PreviousAction() { putValue(Action.NAME, CheckStyleBundle.message("config.file.previous.text")); putValue(Action.SHORT_DESCRIPTION, CheckStyleBundle.message("config.file.previous.tooltip")); putValue(Action.LONG_DESCRIPTION, CheckStyleBundle.message("config.file.previous.tooltip")); } @Override public void actionPerformed(final ActionEvent e) { previousButton.setEnabled(false); switch (currentStep) { case PROPERTIES: moveToStep(Step.SELECT); return; case COMPLETE: case ERROR: try { final Map<String, String> properties = configurationLocation.getProperties(); if (properties == null || properties.isEmpty()) { moveToStep(Step.SELECT); } else { moveToStep(Step.PROPERTIES); } } catch (IOException e1) { moveToStep(Step.SELECT); } return; default: throw new IllegalStateException("Unexpected previous call for step " + currentStep); } } } /** * Respond to a cancel action. */ private class CancelAction extends AbstractAction { private static final long serialVersionUID = -994620715558602656L; CancelAction() { putValue(Action.NAME, CheckStyleBundle.message("config.file.cancel.text")); putValue(Action.SHORT_DESCRIPTION, CheckStyleBundle.message("config.file.cancel.tooltip")); putValue(Action.LONG_DESCRIPTION, CheckStyleBundle.message("config.file.cancel.tooltip")); } @Override public void actionPerformed(final ActionEvent e) { committed = false; setVisible(false); } } }