/** * File: GUI.java * Author: Brian Borowski * Date created: May 1999 * Date last modified: July 6, 2011 */ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Event; import java.awt.FlowLayout; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.Insets; import java.awt.Point; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.KeyStroke; import javax.swing.Timer; import javax.swing.border.BevelBorder; import javax.swing.border.EtchedBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; public class GUI extends JFrame implements ActionListener { public static final String APP_NAME = "Game of Life", IDLE_MESSAGE = "Click on the cells where you want \"life\" to exist.", RUNNING_MESSAGE = "Game in progress..."; public static final int FPS_MIN = 0, FPS_MAX = 30, FPS_INIT = 10; private static final long serialVersionUID = 1L; private final ApplicationStarter applicationStarter; private final JButton startResetButton; private final JComboBox presetComboBox, boardTypeComboBox; private final JLabel statusLabel, generationLabel; private final JSlider slider; private final LifeBoard lifeBoard; private final Timer timer; private long generationNumber; private boolean isGameRunning; public GUI(final ApplicationStarter appStarter) { super(APP_NAME); this.applicationStarter = appStarter; this.lifeBoard = new LifeBoard(LifeBoard.FINITE, "images/smallSmile.gif", 20, 38); // Set up timer to drive animation events. timer = new Timer(fpsToMs(FPS_INIT), this); setJMenuBar(getCreatedMenuBar()); startResetButton = new JButton("Start"); startResetButton.setMnemonic('S'); startResetButton.setPreferredSize(new Dimension(84, 26)); startResetButton.addActionListener(new StartResetActionListener(this)); final JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new FlowLayout()); buttonPanel.add(startResetButton); final JPanel boardPanel = new JPanel(); boardPanel.setLayout(new FlowLayout()); boardPanel.add(lifeBoard); final JPanel mainPanel = new JPanel(); mainPanel.setLayout(new BorderLayout()); mainPanel.add(boardPanel, BorderLayout.CENTER); mainPanel.add(buttonPanel, BorderLayout.SOUTH); mainPanel.setBorder(BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), "Playing Field")); final JLabel blankLabel = new JLabel(); boardTypeComboBox = new JComboBox(); boardTypeComboBox.setBackground(Color.WHITE); for (int i = 0; i < LifeBoard.BOARDTYPES.length; ++i) { boardTypeComboBox.addItem(LifeBoard.BOARDTYPES[i]); } boardTypeComboBox.addItemListener(new ItemListener() { public void itemStateChanged(final ItemEvent ie) { if (ie.getStateChange() == ItemEvent.SELECTED) { lifeBoard.setBoardType(boardTypeComboBox.getSelectedIndex()); } } }); final JLabel boardTypeLabel = new JLabel("Board Type:", JLabel.RIGHT); boardTypeLabel.setDisplayedMnemonic('B'); boardTypeLabel.setLabelFor(boardTypeComboBox); final String[] presetConfigs = ConfigurationManager.getListing("config/Listing"); presetComboBox = new JComboBox(); presetComboBox.setBackground(Color.WHITE); presetComboBox.addItem("Blank board"); presetComboBox.addItem("Random board"); for (int i = 0; i < presetConfigs.length; ++i) { presetComboBox.addItem(presetConfigs[i]); } presetComboBox.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { lifeBoard.clear(); if (presetComboBox.getSelectedIndex() == 1) { lifeBoard.setRandomConfig(); } else if (presetComboBox.getSelectedIndex() > 1) { lifeBoard.setPresetConfig( ConfigurationManager.getConfiguration( (String)presetComboBox.getSelectedItem())); } } }); final JLabel configurationsLabel = new JLabel("Configurations:", JLabel.RIGHT); configurationsLabel.setDisplayedMnemonic('C'); configurationsLabel.setLabelFor(presetComboBox); slider = new JSlider(JSlider.HORIZONTAL, FPS_MIN, FPS_MAX, FPS_INIT); slider.setMajorTickSpacing(10); slider.setMinorTickSpacing(1); slider.setPaintTicks(true); slider.setPaintLabels(true); slider.setFont(new Font("Serif", Font.ITALIC, 12)); slider.addChangeListener(new ChangeListener() { public void stateChanged(final ChangeEvent e) { final JSlider source = (JSlider)e.getSource(); if (!source.getValueIsAdjusting()) { setSpeed(source.getValue()); } } }); final JLabel speedLabel = new JLabel("Speed (FPS):", JLabel.RIGHT); speedLabel.setDisplayedMnemonic('F'); speedLabel.setLabelFor(slider); final JPanel controlPanel = new JPanel(); controlPanel.setLayout(new FlowLayout()); controlPanel.setBorder(BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), "Options")); controlPanel.add(boardTypeLabel); controlPanel.add(boardTypeComboBox); controlPanel.add(blankLabel); controlPanel.add(configurationsLabel); controlPanel.add(presetComboBox); controlPanel.add(blankLabel); controlPanel.add(speedLabel); controlPanel.add(slider); statusLabel = new JLabel(IDLE_MESSAGE); generationLabel = new JLabel(); final GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(0, 0, 2, 0); final JPanel statusPanel1 = new JPanel(); statusPanel1.setLayout(new GridBagLayout()); statusPanel1.setBorder(new BevelBorder(BevelBorder.LOWERED)); statusPanel1.add(statusLabel, gbc); final JPanel statusPanel2 = new JPanel(); statusPanel2.setLayout(new GridBagLayout()); statusPanel2.setBorder(new BevelBorder(BevelBorder.LOWERED)); statusPanel2.add(generationLabel, gbc); final JPanel statusPanel = new JPanel(); statusPanel.setLayout(new GridLayout(1, 2)); statusPanel.add(statusPanel1); statusPanel.add(statusPanel2); final Container contentPane = getContentPane(); contentPane.setLayout(new BorderLayout()); contentPane.add(mainPanel, BorderLayout.PAGE_START); contentPane.add(controlPanel, BorderLayout.CENTER); contentPane.add(statusPanel, BorderLayout.PAGE_END); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); addWindowListener(new ClosingWindowListener(this)); validate(); pack(); setResizable(false); final Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); final Point p = new Point((int)((d.getWidth() - this.getWidth())/2), (int)((d.getHeight() - this.getHeight())/2)); setLocation(p); setVisible(true); } public void start() { timer.restart(); } public void stop() { timer.stop(); } public void actionPerformed(final ActionEvent e) { lifeBoard.makeNextGeneration(); generationLabel.setText("Generation: " + ++generationNumber); if (!lifeBoard.isAnyCellAlive()) { final String message = generationNumber == 2 ? "Your configuration killed everyone off in 1 generation." : "Your configuration killed everyone off in " + (generationNumber - 1) + " generations."; new MessageBox(this, "Too Bad", message, MessageBox.FROWN); resetGame(); } } private JMenuBar getCreatedMenuBar() { final JMenuBar menuBar = new JMenuBar(); final JMenu fileMenu = new JMenu("File"); fileMenu.setMnemonic('F'); KeyStroke ks; final JMenuItem exitItem = new JMenuItem("Exit"); ks = KeyStroke.getKeyStroke(KeyEvent.VK_X, Event.ALT_MASK); exitItem.setAccelerator(ks); exitItem.setMnemonic(KeyEvent.VK_X); exitItem.addActionListener(new ExitActionListener(this)); fileMenu.add(exitItem); final JMenuItem aboutItem = new JMenuItem("About"); aboutItem.setMnemonic('A'); aboutItem.addActionListener(new AboutActionListener(this)); final JMenu helpMenu = new JMenu("Help"); helpMenu.setMnemonic('H'); helpMenu.add(aboutItem); menuBar.add(fileMenu); menuBar.add(helpMenu); return menuBar; } private void setSpeed(final int fps) { if (fps == 0) { stop(); } else { if (isGameRunning && !timer.isRunning()) { start(); } timer.setDelay(fpsToMs(fps)); } } private int fpsToMs(final int fps) { if (fps == 0) { return Integer.MAX_VALUE; } return 1000 / fps; } private void doApplicationClosing(final JFrame parent) { if (applicationStarter != null) { applicationStarter.close(); } else { System.exit(0); } } private void startGame() { isGameRunning = true; startResetButton.setText("Reset"); startResetButton.setMnemonic('R'); statusLabel.setText("Game in session..."); boardTypeComboBox.setEnabled(false); presetComboBox.setEnabled(false); lifeBoard.setEditable(false); generationNumber = 1; generationLabel.setText("Generation: " + generationNumber); setSpeed(slider.getValue()); } private void resetGame() { stop(); isGameRunning = false; startResetButton.setText("Start"); startResetButton.setMnemonic('S'); boardTypeComboBox.setEnabled(true); presetComboBox.setEnabled(true); presetComboBox.setSelectedIndex(0); generationLabel.setText(""); statusLabel.setText(IDLE_MESSAGE); lifeBoard.clear(); lifeBoard.setEditable(true); } class StartResetActionListener implements ActionListener { private final JFrame parent; public StartResetActionListener(final JFrame parent) { this.parent = parent; } public void actionPerformed(final ActionEvent e) { if (!lifeBoard.isAnyCellAlive()) { new MessageBox(parent, "Error", "Board has not been initialized.", MessageBox.EXCLAIM); return; } if (startResetButton.getText().equals("Start")) { startGame(); } else { resetGame(); } } } class ExitActionListener implements ActionListener { private final JFrame parent; public ExitActionListener(final JFrame parent) { this.parent = parent; } public void actionPerformed(final ActionEvent e) { doApplicationClosing(parent); } } class AboutActionListener implements ActionListener { private final JFrame parent; public AboutActionListener(final JFrame parent) { this.parent = parent; } public void actionPerformed(final ActionEvent e) { final String[] data = {"Version 2.0.5", "© 1999-2002, 2011 Brian S. Borowski", "Concept by John H. Conway", "Examples by Kruse and Ryba", "All Rights Reserved."}; new AboutDialog(parent, APP_NAME, data, "images/information.gif"); } } class ClosingWindowListener implements WindowListener { private final JFrame parent; public ClosingWindowListener(final JFrame parent) { this.parent = parent; } public void windowClosing(final WindowEvent e) { doApplicationClosing(parent); } public void windowDeactivated(final WindowEvent e) { } public void windowActivated(final WindowEvent e) { } public void windowDeiconified(final WindowEvent e) { } public void windowIconified(final WindowEvent e) { } public void windowClosed(final WindowEvent e) { } public void windowOpened(final WindowEvent e) { } } }