/******************************************************************************* * Copyright (c) 2001, 2010 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 * * Contributors: * Mathew A. Nelson * - Initial API and implementation * Flemming N. Larsen * - Rewritten to compact code * - Added Javadoc comments * - Added Paint button and isPaintEnabled() * - Added Robocode SG check box and isSGPaintEnabled() for enabling Robocode * SG compatibility * - Updated to use methods from the WindowUtil, which replaces window methods * that have been (re)moved from the robocode.util.Utils class * - Added Pause button *******************************************************************************/ package net.sf.robocode.ui.dialog; import net.sf.robocode.battle.IBattleManager; import net.sf.robocode.ui.IWindowManager; import robocode.control.events.*; import robocode.control.snapshot.IDebugProperty; import robocode.control.snapshot.IRobotSnapshot; import robocode.control.snapshot.ITurnSnapshot; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Hashtable; import java.util.Map; /** * @author Mathew A. Nelson (original) * @author Flemming N. Larsen (contributor) */ @SuppressWarnings("serial") public class RobotDialog extends JFrame { private final Color grayGreen = new Color(0x0080C080); private RobotButton robotButton; private JTabbedPane tabbedPane; private ConsoleScrollPane consoleScrollPane; private ConsoleScrollPane propertiesScrollPane; private JPanel robotDialogContentPane; private JPanel buttonPanel; private JButton okButton; private JButton clearButton; private JButton killButton; private JToggleButton paintButton; private JCheckBox sgCheckBox; private JToggleButton pauseButton; private boolean isListening; private int robotIndex; private IRobotSnapshot lastSnapshot; private boolean printSnapshot; private boolean grayGreenButton; private final Hashtable<String, String> debugProperties = new Hashtable<String, String>(); private final BattleObserver battleObserver = new BattleObserver(); private final IWindowManager windowManager; private final IBattleManager battleManager; public RobotDialog(IWindowManager windowManager, IBattleManager battleManager) { super(); this.battleManager = battleManager; this.windowManager = windowManager; } public void setup(RobotButton robotButton) { this.robotButton = robotButton; initialize(); } private void initialize() { robotIndex = robotButton.getRobotIndex(); setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); setContentPane(getRobotDialogContentPane()); if (windowManager.isSlave()) { getKillButton().setEnabled(false); } this.setTitle(robotButton.getRobotName()); pack(); } @Override protected void finalize() throws Throwable { try { detach(); } finally { super.finalize(); } } public void detach() { if (isListening) { windowManager.removeBattleListener(battleObserver); isListening = false; } robotButton.detach(); getPauseButton().setEnabled(false); getKillButton().setEnabled(false); lastSnapshot = null; printSnapshot(); } public void attach(RobotButton robotButton) { this.robotButton = robotButton; robotIndex = this.robotButton.getRobotIndex(); if (!isListening) { isListening = true; windowManager.addBattleListener(battleObserver); } getPauseButton().setEnabled(true); if (!windowManager.isSlave()) { getKillButton().setEnabled(true); } } public void reset() { getConsoleScrollPane().setText(null); lastSnapshot = null; debugProperties.clear(); } /** * When robotDialog is packed, we want to set a reasonable size. However, * after that, we need a null preferred size so the scrollpane will scroll. * (preferred size should be based on the text inside) */ @Override public void pack() { getConsoleScrollPane().setPreferredSize(new Dimension(426, 200)); super.pack(); getTabbedPane().setPreferredSize(null); } /** * Returns true if Paint is enabled; false otherwise * * @return true if Paint is enabled; false otherwise */ public boolean isPaintEnabled() { return getPaintButton().isSelected(); } /** * Returns true if the Robocode SG paint is enabled; false otherwise * * @return true if the Robocode SG paint enabled; false otherwise */ public boolean isSGPaintEnabled() { return getSGCheckBox().isSelected(); } private final transient ActionListener eventHandler = new ActionListener() { public void actionPerformed(ActionEvent e) { Object src = e.getSource(); if (src == RobotDialog.this.getOkButton()) { okButtonActionPerformed(); } else if (src == RobotDialog.this.getClearButton()) { clearButtonActionPerformed(); } else if (src == RobotDialog.this.getKillButton()) { killButtonActionPerformed(); } else if (src == RobotDialog.this.getPaintButton()) { paintButtonActionPerformed(); } else if (src == RobotDialog.this.getSGCheckBox()) { sgCheckBoxActionPerformed(); } else if (src == RobotDialog.this.getPauseButton()) { pauseResumeButtonActionPerformed(); } } }; /** * Returns the dialog's content pane * * @return the dialog's content pane */ private JPanel getRobotDialogContentPane() { if (robotDialogContentPane == null) { robotDialogContentPane = new JPanel(); robotDialogContentPane.setLayout(new BorderLayout()); robotDialogContentPane.add(getTabbedPane()); robotDialogContentPane.add(getButtonPanel(), BorderLayout.SOUTH); } return robotDialogContentPane; } private JTabbedPane getTabbedPane() { if (tabbedPane == null) { tabbedPane = new JTabbedPane(); tabbedPane.setLayout(new BorderLayout()); tabbedPane.addTab("Console", getConsoleScrollPane()); tabbedPane.addTab("Properties", getPropertiesScrollPane()); tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); tabbedPane.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { printSnapshot = (tabbedPane.getSelectedIndex() == 1); printSnapshot(); } }); } return tabbedPane; } private void printSnapshot() { if (printSnapshot) { String text = null; if (lastSnapshot != null) { StringBuilder sb = new StringBuilder(); sb.append("energy: ").append(lastSnapshot.getEnergy()).append('\n'); sb.append("x: ").append(lastSnapshot.getX()).append('\n'); sb.append("y: ").append(lastSnapshot.getY()).append('\n'); sb.append("velocity: ").append(lastSnapshot.getVelocity()).append('\n'); sb.append("heat: ").append(lastSnapshot.getGunHeat()).append('\n'); sb.append("bodyHeading: rad: ").append(lastSnapshot.getBodyHeading()).append(" deg: ").append(Math.toDegrees(lastSnapshot.getBodyHeading())).append( '\n'); sb.append("gunHeading: rad: ").append(lastSnapshot.getGunHeading()).append(" deg: ").append(Math.toDegrees(lastSnapshot.getGunHeading())).append( '\n'); sb.append("radarHeading: rad: ").append(lastSnapshot.getRadarHeading()).append(" deg: ").append(Math.toDegrees(lastSnapshot.getRadarHeading())).append( '\n'); sb.append("state: ").append(lastSnapshot.getState()).append('\n'); sb.append('\n'); IDebugProperty[] debugPropeties = lastSnapshot.getDebugProperties(); if (debugPropeties != null) { for (IDebugProperty prop : debugPropeties) { if (prop.getValue() == null || prop.getValue().length() == 0) { debugProperties.remove(prop.getKey()); } else { debugProperties.put(prop.getKey(), prop.getValue()); } } } for (Map.Entry<String, String> prop : debugProperties.entrySet()) { sb.append(prop.getKey()).append(": ").append(prop.getValue()).append('\n'); } text = sb.toString(); } getPropertiesScrollPane().setText(text); } } private ConsoleScrollPane getPropertiesScrollPane() { if (propertiesScrollPane == null) { propertiesScrollPane = new ConsoleScrollPane(); } return propertiesScrollPane; } /** * Returns the console scroll pane * * @return the console scroll pane */ private ConsoleScrollPane getConsoleScrollPane() { if (consoleScrollPane == null) { consoleScrollPane = new ConsoleScrollPane(); } return consoleScrollPane; } /** * Returns the button panel * * @return the button panel */ private JPanel getButtonPanel() { if (buttonPanel == null) { buttonPanel = new JPanel(); buttonPanel.setLayout(new FlowLayout(FlowLayout.LEFT)); buttonPanel.add(getOkButton()); buttonPanel.add(getClearButton()); buttonPanel.add(getKillButton()); buttonPanel.add(getPaintButton()); buttonPanel.add(getSGCheckBox()); buttonPanel.add(getPauseButton()); } return buttonPanel; } /** * Returns the OK button * * @return the OK button */ private JButton getOkButton() { if (okButton == null) { okButton = getNewButton("OK"); } return okButton; } /** * Returns the Clear button * * @return the Clear button */ private JButton getClearButton() { if (clearButton == null) { clearButton = getNewButton("Clear"); } return clearButton; } /** * Returns the Kill button. * * @return the Kill button */ private JButton getKillButton() { if (killButton == null) { killButton = getNewButton("Kill Robot"); } return killButton; } /** * Returns the Paint button. * * @return the Paint button */ private JToggleButton getPaintButton() { if (paintButton == null) { paintButton = new JToggleButton("Paint"); paintButton.addActionListener(eventHandler); } return paintButton; } /** * Returns the SG checkbox. * * @return the SG checkbox */ private JCheckBox getSGCheckBox() { if (sgCheckBox == null) { sgCheckBox = new JCheckBox("Robocode SG"); sgCheckBox.addActionListener(eventHandler); } return sgCheckBox; } /** * Returns the Pause button. * * @return the Pause button */ private JToggleButton getPauseButton() { if (pauseButton == null) { pauseButton = new JToggleButton("Pause/Debug"); pauseButton.addActionListener(eventHandler); } return pauseButton; } /** * Returns a new button with event handler and with the specified text * * @param text The text of the button * @return a new button with event handler and with the specified text */ private JButton getNewButton(String text) { JButton button = new JButton(text); button.addActionListener(eventHandler); return button; } /** * Is called when the OK button has been activated */ private void okButtonActionPerformed() { dispose(); } /** * Is called when the Clear button has been activated */ private void clearButtonActionPerformed() { reset(); } /** * Is called when the Kill button has been activated */ private void killButtonActionPerformed() { battleManager.killRobot(robotIndex); } /** * Is called when the Paint button has been activated */ private void paintButtonActionPerformed() { battleManager.setPaintEnabled(robotIndex, getPaintButton().isSelected()); } /** * Is called when the SG check box has been activated */ private void sgCheckBoxActionPerformed() { battleManager.setSGPaintEnabled(robotIndex, getSGCheckBox().isSelected()); } /** * Is called when the Pause/Resume button has been activated */ private void pauseResumeButtonActionPerformed() { battleManager.togglePauseResumeBattle(); } private class BattleObserver extends BattleAdaptor { @Override public void onBattlePaused(BattlePausedEvent event) { getPauseButton().setSelected(true); } @Override public void onBattleResumed(BattleResumedEvent event) { getPauseButton().setSelected(false); } @Override public void onTurnEnded(TurnEndedEvent event) { final ITurnSnapshot turn = event.getTurnSnapshot(); if (turn == null) { return; } lastSnapshot = turn.getRobots()[robotIndex]; final String text = lastSnapshot.getOutputStreamSnapshot(); if (text != null && text.length() > 0) { getConsoleScrollPane().append(text); getConsoleScrollPane().scrollToBottom(); } if (lastSnapshot.isPaintRobot() && !grayGreenButton) { grayGreenButton = true; getPaintButton().setBackground(grayGreen); } printSnapshot(); } } }