/**
* Copyright (c) 2001-2017 Mathew A. Nelson and Robocode contributors
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://robocode.sourceforge.net/license/epl-v10.html
*/
package net.sf.robocode.ui.dialog;
import javax.swing.*;
import javax.swing.GroupLayout.Alignment;
import javax.swing.border.Border;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import net.sf.robocode.battle.BattleProperties;
import net.sf.robocode.settings.ISettingsManager;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* @author Mathew A. Nelson (original)
* @author Flemming N. Larsen (original)
*/
@SuppressWarnings("serial")
public class NewBattleRulesTab extends JPanel {
private final static int MIN_BATTLEFIELD_SIZE = 400;
private final static int MAX_BATTLEFIELD_SIZE = 5000;
private final static int BATTLEFIELD_STEP_SIZE = 100;
private ISettingsManager settingsManager;
private BattleProperties battleProperties;
private final EventHandler eventHandler = new EventHandler();
private SizeButton[] predefinedSizeButtons = {
new SizeButton(400, 400), new SizeButton(600, 400), new SizeButton(600, 600), new SizeButton(800, 600),
new SizeButton(800, 800), new SizeButton(1000, 800), new SizeButton(1000, 1000), new SizeButton(1200, 1200),
new SizeButton(2000, 2000), new SizeButton(5000, 5000)
};
private final JLabel numberOfRoundsLabel = new JLabel("Number of Rounds:");
private final JLabel gunCoolingRateLabel = new JLabel("Gun Cooling Rate:");
private final JLabel inactivityTimeLabel = new JLabel("Inactivity Time:");
private final JLabel sentryBorderSizeLabel = new JLabel("Sentry Border Size");
private final JLabel hideEnemyNamesLabel = new JLabel("Hide Enemy Names:");
private final JButton restoreDefaultsButton = new JButton("Restore Defaults");
private JTextField numberOfRoundsTextField;
private JTextField gunCoolingRateTextField;
private JTextField inactivityTimeTextField;
private JTextField sentryBorderSizeTextField;
private final JCheckBox hideEnemyNamesCheckBox = new JCheckBox();
private JSlider battlefieldWidthSlider;
private JSlider battlefieldHeightSlider;
private JLabel battlefieldSizeLabel;
public NewBattleRulesTab() {
super();
}
public void setup(ISettingsManager settingsManager, BattleProperties battleProperties) {
this.settingsManager = settingsManager;
this.battleProperties = battleProperties;
EventHandler eventHandler = new EventHandler();
battlefieldWidthSlider = createBattlefieldSizeSlider();
battlefieldWidthSlider.setOrientation(SwingConstants.HORIZONTAL);
battlefieldHeightSlider = createBattlefieldSizeSlider();
battlefieldHeightSlider.setOrientation(SwingConstants.VERTICAL);
battlefieldHeightSlider.setInverted(true);
battlefieldSizeLabel = new BattlefieldSizeLabel();
battlefieldSizeLabel.setHorizontalAlignment(SwingConstants.CENTER);
battlefieldSizeLabel.setMinimumSize(new Dimension(50, 50));
battlefieldSizeLabel.setMaximumSize(new Dimension(500, 500));
battlefieldWidthSlider.addChangeListener(eventHandler);
battlefieldHeightSlider.addChangeListener(eventHandler);
JPanel rulesPanel = createRulesPanel();
rulesPanel.addAncestorListener(eventHandler);
restoreDefaultsButton.addActionListener(eventHandler);
setLayout(new BorderLayout());
add(rulesPanel, BorderLayout.WEST);
add(restoreDefaultsButton, BorderLayout.SOUTH);
add(createBattlefieldSizePanel(), BorderLayout.CENTER);
}
private JPanel createBattlefieldSizePanel() {
JPanel panel = new JPanel();
panel.addAncestorListener(eventHandler);
Border border = BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Battlefield Size"),
BorderFactory.createEmptyBorder(10, 10, 10, 10));
panel.setBorder(border);
panel.setLayout(new BorderLayout());
JPanel sliderPanel = createBattlefieldSlidersPanel();
panel.add(sliderPanel, BorderLayout.CENTER);
JPanel buttonsPanel = createPredefinedSizesPanel();
panel.add(buttonsPanel, BorderLayout.EAST);
return panel;
}
private JPanel createBattlefieldSlidersPanel() {
JPanel panel = new JPanel();
GroupLayout layout = new GroupLayout(panel);
panel.setLayout(layout);
GroupLayout.SequentialGroup leftToRight = layout.createSequentialGroup();
GroupLayout.ParallelGroup left = layout.createParallelGroup();
left.addComponent(battlefieldSizeLabel);
left.addComponent(battlefieldWidthSlider);
leftToRight.addGroup(left);
GroupLayout.ParallelGroup right = layout.createParallelGroup();
right.addComponent(battlefieldHeightSlider);
leftToRight.addGroup(right);
GroupLayout.SequentialGroup topToBottom = layout.createSequentialGroup();
GroupLayout.ParallelGroup top = layout.createParallelGroup();
top.addComponent(battlefieldSizeLabel);
top.addComponent(battlefieldHeightSlider);
topToBottom.addGroup(top);
GroupLayout.ParallelGroup bottom = layout.createParallelGroup();
bottom.addComponent(battlefieldWidthSlider);
topToBottom.addGroup(bottom);
layout.setHorizontalGroup(leftToRight);
layout.setVerticalGroup(topToBottom);
return panel;
}
private JPanel createPredefinedSizesPanel() {
JPanel panel = new JPanel();
Border border = BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0),
BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Predefined Sizes"));
panel.setBorder(border);
panel.setLayout(new GridLayout(predefinedSizeButtons.length, 1));
for (SizeButton button : predefinedSizeButtons) {
panel.add(button);
}
return panel;
}
private JPanel createRulesPanel() {
JPanel panel = new JPanel();
panel.addAncestorListener(new EventHandler());
panel.setBorder(BorderFactory.createEtchedBorder());
GroupLayout layout = new GroupLayout(panel);
layout.setAutoCreateContainerGaps(true);
layout.setAutoCreateGaps(true);
panel.setLayout(layout);
GroupLayout.SequentialGroup leftToRight = layout.createSequentialGroup();
GroupLayout.ParallelGroup left = layout.createParallelGroup();
left.addComponent(numberOfRoundsLabel);
left.addComponent(gunCoolingRateLabel);
left.addComponent(inactivityTimeLabel);
left.addComponent(sentryBorderSizeLabel);
left.addComponent(hideEnemyNamesLabel);
leftToRight.addGroup(left);
GroupLayout.ParallelGroup right = layout.createParallelGroup();
right.addComponent(getNumberOfRoundsTextField());
right.addComponent(getGunCoolingRateTextField());
right.addComponent(getInactivityTimeTextField());
right.addComponent(getSentryBorderSizeTextField());
right.addComponent(hideEnemyNamesCheckBox);
leftToRight.addGroup(right);
GroupLayout.SequentialGroup topToBottom = layout.createSequentialGroup();
GroupLayout.ParallelGroup row0 = layout.createParallelGroup(Alignment.BASELINE);
row0.addComponent(numberOfRoundsLabel);
row0.addComponent(numberOfRoundsTextField);
topToBottom.addGroup(row0);
GroupLayout.ParallelGroup row1 = layout.createParallelGroup(Alignment.BASELINE);
row1.addComponent(gunCoolingRateLabel);
row1.addComponent(getGunCoolingRateTextField());
topToBottom.addGroup(row1);
GroupLayout.ParallelGroup row2 = layout.createParallelGroup(Alignment.BASELINE);
row2.addComponent(inactivityTimeLabel);
row2.addComponent(inactivityTimeTextField);
topToBottom.addGroup(row2);
GroupLayout.ParallelGroup row3 = layout.createParallelGroup(Alignment.BASELINE);
row3.addComponent(sentryBorderSizeLabel);
row3.addComponent(sentryBorderSizeTextField);
topToBottom.addGroup(row3);
GroupLayout.ParallelGroup row4 = layout.createParallelGroup(Alignment.CENTER);
row4.addComponent(hideEnemyNamesLabel);
row4.addComponent(hideEnemyNamesCheckBox);
topToBottom.addGroup(row4);
layout.setHorizontalGroup(leftToRight);
layout.setVerticalGroup(topToBottom);
return panel;
}
private JTextField getNumberOfRoundsTextField() {
if (numberOfRoundsTextField == null) {
numberOfRoundsTextField = new JTextField(5);
numberOfRoundsTextField.setText("" + battleProperties.getNumRounds());
numberOfRoundsTextField.setInputVerifier(
new InputVerifier() {
@Override
public boolean verify(JComponent input) {
boolean isValid = false;
String text = ((JTextField) input).getText();
if (text != null && text.matches("\\d+")) {
int numRounds = Integer.parseInt(text);
isValid = (numRounds > 0);
}
if (!isValid) {
WindowUtil.messageError(
"'Number of Rounds' must be an integer value > 0.\n" + "Default value is 10.");
numberOfRoundsTextField.setText("" + battleProperties.getNumRounds());
}
return isValid;
}
});
}
return numberOfRoundsTextField;
}
private JTextField getGunCoolingRateTextField() {
if (gunCoolingRateTextField == null) {
gunCoolingRateTextField = new JTextField(5);
gunCoolingRateTextField.setText("" + battleProperties.getGunCoolingRate());
gunCoolingRateTextField.setInputVerifier(
new InputVerifier() {
@Override
public boolean verify(JComponent input) {
boolean isValid = false;
String text = ((JTextField) input).getText();
if (text != null && text.matches("\\d*(\\.\\d+)?")) {
double gunCoolingRate = Double.parseDouble(text);
isValid = (gunCoolingRate > 0 && gunCoolingRate <= 0.7);
}
if (!isValid) {
WindowUtil.messageError(
"'Gun Cooling Rate' must be a floating point number > 0 and <= 0.7.\n"
+ "Default value is 0.1.");
gunCoolingRateTextField.setText("" + battleProperties.getGunCoolingRate());
}
return isValid;
}
});
}
return gunCoolingRateTextField;
}
private JTextField getInactivityTimeTextField() {
if (inactivityTimeTextField == null) {
inactivityTimeTextField = new JTextField(5);
inactivityTimeTextField.setText("" + battleProperties.getInactivityTime());
inactivityTimeTextField.setInputVerifier(
new InputVerifier() {
@Override
public boolean verify(JComponent input) {
boolean isValid = false;
String text = ((JTextField) input).getText();
if (text != null && text.matches("\\d+")) {
int inactivityTime = Integer.parseInt(text);
isValid = (inactivityTime >= 0);
}
if (!isValid) {
WindowUtil.messageError(
"'Inactivity Time' must be an integer value >= 0.\n" + "Default value is 450.");
inactivityTimeTextField.setText("" + battleProperties.getInactivityTime());
}
return isValid;
}
});
}
return inactivityTimeTextField;
}
private JTextField getSentryBorderSizeTextField() {
if (sentryBorderSizeTextField == null) {
sentryBorderSizeTextField = new JTextField(5);
sentryBorderSizeTextField.setText("" + battleProperties.getSentryBorderSize());
sentryBorderSizeTextField.setInputVerifier(
new InputVerifier() {
@Override
public boolean verify(JComponent input) {
boolean isValid = false;
String text = ((JTextField) input).getText();
if (text != null && text.matches("\\d+")) {
int borderSize = Integer.parseInt(text);
isValid = (borderSize >= 50);
}
if (!isValid) {
WindowUtil.messageError(
"'Sentry Border Size' must be an integer value >= 50.\n" + "Default value is 100.");
sentryBorderSizeTextField.setText("" + battleProperties.getSentryBorderSize());
}
return isValid;
}
});
}
return sentryBorderSizeTextField;
}
private JSlider createBattlefieldSizeSlider() {
JSlider slider = new JSlider();
slider.setMinimum(MIN_BATTLEFIELD_SIZE);
slider.setMaximum(MAX_BATTLEFIELD_SIZE);
slider.setMajorTickSpacing(BATTLEFIELD_STEP_SIZE);
slider.setSnapToTicks(true);
slider.setPaintTicks(true);
return slider;
}
private void updateBattlefieldSizeLabel() {
int w = battlefieldWidthSlider.getValue();
int h = battlefieldHeightSlider.getValue();
battlefieldSizeLabel.setText(w + " x " + h);
}
private class SizeButton extends JButton {
final int width;
final int height;
public SizeButton(int width, int height) {
super(width + "x" + height);
this.width = width;
this.height = height;
addActionListener(eventHandler);
}
}
private class EventHandler implements AncestorListener, ActionListener, ChangeListener {
@Override
public void ancestorAdded(AncestorEvent event) {
pushBattlePropertiesToUIComponents();
}
@Override
public void ancestorRemoved(AncestorEvent event) {
Integer numberOfRounds;
try {
numberOfRounds = Integer.parseInt(getNumberOfRoundsTextField().getText());
} catch (NumberFormatException e) {
numberOfRounds = null;
}
if (numberOfRounds != null) {
settingsManager.setBattleDefaultNumberOfRounds(numberOfRounds);
battleProperties.setNumRounds(numberOfRounds);
}
Double gunCoolingRate;
try {
gunCoolingRate = Double.parseDouble(getGunCoolingRateTextField().getText());
} catch (NumberFormatException e) {
gunCoolingRate = null;
}
if (gunCoolingRate != null) {
settingsManager.setBattleDefaultGunCoolingRate(gunCoolingRate);
battleProperties.setGunCoolingRate(gunCoolingRate);
}
Integer inactivityTime;
try {
inactivityTime = Integer.parseInt(getInactivityTimeTextField().getText());
} catch (NumberFormatException e) {
inactivityTime = null;
}
if (inactivityTime != null) {
settingsManager.setBattleDefaultInactivityTime(inactivityTime);
battleProperties.setInactivityTime(inactivityTime);
}
Integer sentryBorderSize;
try {
sentryBorderSize = Integer.parseInt(getSentryBorderSizeTextField().getText());
} catch (NumberFormatException e) {
sentryBorderSize = null;
}
if (sentryBorderSize != null) {
settingsManager.setBattleDefaultSentryBorderSize(sentryBorderSize);
battleProperties.setSentryBorderSize(sentryBorderSize);
}
boolean hideEnemyNames = hideEnemyNamesCheckBox.isSelected();
settingsManager.setBattleDefaultHideEnemyNames(hideEnemyNames);
battleProperties.setHideEnemyNames(hideEnemyNames);
int weight = battlefieldWidthSlider.getValue();
int height = battlefieldHeightSlider.getValue();
settingsManager.setBattleDefaultBattlefieldWidth(weight);
settingsManager.setBattleDefaultBattlefieldHeight(height);
battleProperties.setBattlefieldWidth(weight);
battleProperties.setBattlefieldHeight(height);
}
@Override
public void ancestorMoved(AncestorEvent event) {}
@Override
public void actionPerformed(ActionEvent event) {
if (event.getSource() instanceof SizeButton) {
SizeButton button = (SizeButton) event.getSource();
battlefieldWidthSlider.setValue(button.width);
battlefieldHeightSlider.setValue(button.height);
updateBattlefieldSizeLabel();
} else if (event.getSource() == restoreDefaultsButton) {
battleProperties.setBattlefieldWidth(800);
battleProperties.setBattlefieldHeight(600);
battleProperties.setNumRounds(10);
battleProperties.setGunCoolingRate(0.1);
battleProperties.setInactivityTime(450);
battleProperties.setHideEnemyNames(false);
battleProperties.setSentryBorderSize(100);
pushBattlePropertiesToUIComponents();
}
}
@Override
public void stateChanged(ChangeEvent event) {
if ((event.getSource() == battlefieldWidthSlider) || (event.getSource() == battlefieldHeightSlider)) {
updateBattlefieldSizeLabel();
}
}
private void pushBattlePropertiesToUIComponents() {
battlefieldWidthSlider.setValue(battleProperties.getBattlefieldWidth());
battlefieldHeightSlider.setValue(battleProperties.getBattlefieldHeight());
updateBattlefieldSizeLabel();
getNumberOfRoundsTextField().setText("" + battleProperties.getNumRounds());
getGunCoolingRateTextField().setText("" + battleProperties.getGunCoolingRate());
getInactivityTimeTextField().setText("" + battleProperties.getInactivityTime());
getSentryBorderSizeTextField().setText("" + battleProperties.getSentryBorderSize());
hideEnemyNamesCheckBox.setSelected(battleProperties.getHideEnemyNames());
}
}
private class BattlefieldSizeLabel extends JLabel {
@Override
public void paintComponent(Graphics g) {
g.setColor(SystemColor.activeCaption);
int width = getWidth() * battlefieldWidthSlider.getValue() / MAX_BATTLEFIELD_SIZE;
int height = getHeight() * battlefieldHeightSlider.getValue() / MAX_BATTLEFIELD_SIZE;
g.fillRect(0, 0, width, height);
super.paintComponent(g);
}
}
}