package games.strategy.triplea.ui; 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.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SwingUtilities; import com.google.common.annotations.VisibleForTesting; import games.strategy.engine.data.GameData; import games.strategy.engine.data.PlayerID; import games.strategy.engine.data.util.ResourceCollectionUtils; import games.strategy.sound.ClipPlayer; import games.strategy.sound.SoundPath; import games.strategy.triplea.Constants; import games.strategy.triplea.attachments.UserActionAttachment; import games.strategy.triplea.delegate.remote.IUserActionDelegate; import games.strategy.ui.SwingAction; /** * Similar to PoliticsPanel, but for UserActionAttachment/Delegate. */ public class UserActionPanel extends ActionPanel { private static final long serialVersionUID = -2735582890226625860L; private final JLabel m_actionLabel = new JLabel(); private JButton m_selectUserActionButton = null; private JButton m_doneButton = null; private UserActionAttachment m_choice = null; private final TripleAFrame m_parent; private boolean m_firstRun = true; private List<UserActionAttachment> m_validUserActions = Collections.emptyList(); public UserActionPanel(final GameData data, final MapPanel map, final TripleAFrame parent) { super(data, map); m_parent = parent; } @Override public String toString() { return "Actions and Operations Panel"; } @Override public void display(final PlayerID id) { super.display(id); m_choice = null; SwingUtilities.invokeLater(() -> { removeAll(); m_actionLabel.setText(id.getName() + " Actions and Operations"); add(m_actionLabel); m_selectUserActionButton = new JButton(SelectUserActionAction); m_selectUserActionButton.setEnabled(false); add(m_selectUserActionButton); m_doneButton = new JButton(DontBotherAction); m_doneButton.setEnabled(false); SwingUtilities.invokeLater(() -> m_doneButton.requestFocusInWindow()); add(m_doneButton); }); } /** * waits till someone calls release() and then returns the action chosen. * * @return the choice of action */ public UserActionAttachment waitForUserActionAction(final boolean firstRun, final IUserActionDelegate iUserActionsDelegate) { m_firstRun = firstRun; m_validUserActions = new ArrayList<>(iUserActionsDelegate.getValidActions()); Collections.sort(m_validUserActions, new UserActionComparator()); if (m_validUserActions.isEmpty()) { // No Valid User actions, do nothing return null; } else { if (m_firstRun) { ClipPlayer.play(SoundPath.CLIP_PHASE_USER_ACTIONS, getCurrentPlayer()); } SwingUtilities.invokeLater(() -> { m_selectUserActionButton.setEnabled(true); m_doneButton.setEnabled(true); // press the user action button for us. SelectUserActionAction.actionPerformed(null); }); } waitForRelease(); return m_choice; } /** * Fires up a JDialog showing valid actions, * choosing an action will release this model and trigger waitForRelease(). */ private final Action SelectUserActionAction = new AbstractAction("Take Action...") { private static final long serialVersionUID = 2389485901611958851L; @Override public void actionPerformed(final ActionEvent event) { final JDialog userChoiceDialog = new JDialog(m_parent, "Actions and Operations", true); final JPanel userChoicePanel = new JPanel(); userChoicePanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); userChoicePanel.setLayout(new GridBagLayout()); int row = 0; final JScrollPane choiceScroll = new JScrollPane(getUserActionButtonPanel(userChoiceDialog)); choiceScroll.setBorder(BorderFactory.createEtchedBorder()); choiceScroll.setPreferredSize(getUserActionScrollPanePreferredSize(choiceScroll)); userChoicePanel.add(choiceScroll, new GridBagConstraints(0, row++, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); if (canSpendResourcesOnUserActions(m_validUserActions)) { final JLabel resourcesLabel = new JLabel(String.format("You have %s left", ResourceCollectionUtils.getProductionResources(getCurrentPlayer().getResources()))); userChoicePanel.add(resourcesLabel, new GridBagConstraints(0, row++, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(8, 0, 0, 0), 0, 0)); } final JButton noActionButton = new JButton(SwingAction.of("No Actions", e -> userChoiceDialog.setVisible(false))); SwingUtilities.invokeLater(() -> noActionButton.requestFocusInWindow()); userChoicePanel.add(noActionButton, new GridBagConstraints(0, row, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(12, 0, 0, 0), 0, 0)); userChoiceDialog.add(userChoicePanel); userChoiceDialog.pack(); userChoiceDialog.setLocationRelativeTo(m_parent); userChoiceDialog.setVisible(true); userChoiceDialog.dispose(); } }; @VisibleForTesting static boolean canSpendResourcesOnUserActions(final Collection<UserActionAttachment> userActions) { return userActions.stream().anyMatch(userAction -> userAction.getCostPU() > 0); } private JPanel getUserActionButtonPanel(final JDialog parent) { final JPanel userActionButtonPanel = new JPanel(); userActionButtonPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); userActionButtonPanel.setLayout(new GridBagLayout()); final int firstRow = 0; final int lastRow = m_validUserActions.size() - 1; int row = 0; for (final UserActionAttachment uaa : m_validUserActions) { final int topInset = (row == firstRow) ? 0 : 4; final int bottomInset = (row == lastRow) ? 0 : 4; final boolean canPlayerAffordUserAction = canPlayerAffordUserAction(getCurrentPlayer(), uaa); userActionButtonPanel.add(getOtherPlayerFlags(uaa), new GridBagConstraints(0, row, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(topInset, 0, bottomInset, 4), 0, 0)); final JButton button = new JButton(getActionButtonText(uaa)); button.addActionListener(ae -> { m_selectUserActionButton.setEnabled(false); m_doneButton.setEnabled(false); m_validUserActions = Collections.emptyList(); m_choice = uaa; parent.setVisible(false); release(); }); button.setEnabled(canPlayerAffordUserAction); userActionButtonPanel.add(button, new GridBagConstraints(1, row, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(topInset, 4, bottomInset, 4), 0, 0)); final JLabel descriptionLabel = getActionDescriptionLabel(uaa); descriptionLabel.setEnabled(canPlayerAffordUserAction); userActionButtonPanel.add(descriptionLabel, new GridBagConstraints(2, row, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(topInset, 4, bottomInset, 0), 0, 0)); row++; } return userActionButtonPanel; } @VisibleForTesting static boolean canPlayerAffordUserAction(final PlayerID player, final UserActionAttachment userAction) { return userAction.getCostPU() <= player.getResources().getQuantity(Constants.PUS); } private static Dimension getUserActionScrollPanePreferredSize(final JScrollPane scrollPane) { final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); final int availableHeight = screenSize.height - 120; final int availableWidth = screenSize.width - 30; return new Dimension( (scrollPane.getPreferredSize().width > availableWidth ? availableWidth : (scrollPane.getPreferredSize().width + (scrollPane.getPreferredSize().height > availableHeight ? 25 : 0))), (scrollPane.getPreferredSize().height > availableHeight ? availableHeight : (scrollPane.getPreferredSize().height) + (scrollPane.getPreferredSize().width > availableWidth ? 25 : 0))); } /** * This will stop the user action Phase. */ private final Action DontBotherAction = new AbstractAction("Done") { private static final long serialVersionUID = 2835948679299520899L; @Override public void actionPerformed(final ActionEvent event) { if (!m_firstRun || youSureDoNothing()) { m_choice = null; release(); } } private boolean youSureDoNothing() { final int rVal = JOptionPane.showConfirmDialog(JOptionPane.getFrameForComponent(UserActionPanel.this), "Are you sure you dont want to do anything?", "End Actions", JOptionPane.YES_NO_OPTION); return rVal == JOptionPane.YES_OPTION; } }; /** * Convenient method to get a JCompenent showing the flags involved in this * action. * * @param uaa * the User action attachment to get the "otherflags" for * @return a JComponent with the flags involved. */ private JPanel getOtherPlayerFlags(final UserActionAttachment uaa) { final JPanel panel = new JPanel(); for (final PlayerID p : uaa.getOtherPlayers()) { panel.add(new JLabel(new ImageIcon(this.getMap().getUIContext().getFlagImageFactory().getFlag(p)))); } return panel; } private static String getActionButtonText(final UserActionAttachment paa) { final String costString = paa.getCostPU() == 0 ? "" : "[" + paa.getCostPU() + " PU] "; return costString + UserActionText.getInstance().getButtonText(paa.getText()); } private static JLabel getActionDescriptionLabel(final UserActionAttachment paa) { final String chanceString = paa.getChanceToHit() >= paa.getChanceDiceSides() ? "" : "[" + paa.getChanceToHit() + "/" + paa.getChanceDiceSides() + "] "; return new JLabel(chanceString + UserActionText.getInstance().getDescription(paa.getText())); } private static final class UserActionComparator implements Comparator<UserActionAttachment> { @Override public int compare(final UserActionAttachment uaa1, final UserActionAttachment uaa2) { if (uaa1.equals(uaa2)) { return 0; } return uaa1.getName().compareTo(uaa2.getName()); } } }