/** * 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); } } }