/** * Graphical is the startup class and controller for * the java swing version of Life. * @author David Hutchens * @date August 2008 */ import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; public class Graphical extends JPanel implements ActionListener, ChangeListener { private static Graphical sharedApp = null; private static final long serialVersionUID = 1L; private static final int squareSize = 5; private static final int borderSize = 5; private static final String runButtonText = "Run"; private static final String birthMinSliderName = "Birth Minimum"; private static final String birthMaxSliderName = "Birth Maximum"; private static final String liveMinSliderName = "Live Minimum"; private static final String liveMaxSliderName = "Live Maximum"; private Timer myTimer; private JFrame theFrame; private Life myLife; private int birthMin, birthMax, liveMin, liveMax; /** * Constructs the object, creating the model and view (with the * frames and widgets) and starts the timer. */ private Graphical() { //Set the look and feel (for Macs too). if (System.getProperty("mrj.version") != null) { System.setProperty("apple.laf.useScreenMenuBar","true"); } JFrame.setDefaultLookAndFeelDecorated(true); myLife = null; birthMin = 4; birthMax = 6; liveMin = 3; liveMax = 5; theFrame = new JFrame("Life"); theFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel widgetPanel = setupLayout(); theFrame.getContentPane().add(widgetPanel, BorderLayout.CENTER); //createTextItems(widgetPanel); createButtons(widgetPanel); theFrame.pack(); theFrame.setVisible(true); myTimer = new Timer(1000, this); myTimer.start(); } /** * Set up the layout for the interface * @return returns the JPanel that will hold the widgets and this UI */ private JPanel setupLayout() { JPanel widgetPanel = new JPanel(); widgetPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); widgetPanel.setLayout(new BoxLayout(widgetPanel, BoxLayout.PAGE_AXIS)); widgetPanel.add(this); Dimension puzzleDrawingSize = new Dimension(100*squareSize + 2*borderSize, 100*squareSize + 2*borderSize); setMinimumSize(puzzleDrawingSize); setPreferredSize(puzzleDrawingSize); setMaximumSize(puzzleDrawingSize); setBorder(BorderFactory.createLineBorder(Color.lightGray, 2)); return widgetPanel; } /** * Creates the buttons to control action. * @param widgetPanel is the JPanel that will hold the buttons. */ private void createButtons(JPanel widgetPanel) { JButton runButton = new JButton(runButtonText); runButton.addActionListener(this); widgetPanel.add(runButton); runButton.setAlignmentX(CENTER_ALIGNMENT); createSlider(widgetPanel, birthMinSliderName, birthMin); createSlider(widgetPanel, birthMaxSliderName, birthMax); createSlider(widgetPanel, liveMinSliderName, liveMin); createSlider(widgetPanel, liveMaxSliderName, liveMax); } /** * Creates a slider and adds it to the widgetPanel with a label * @param widgetPanel panel that will contain the slider * @param name of the slider * @param initialValue of the slider */ private void createSlider(JPanel widgetPanel, String name, int initialValue) { JPanel p = new JPanel(); p.setAlignmentX(CENTER_ALIGNMENT); p.setLayout(new BoxLayout(p, BoxLayout.LINE_AXIS)); widgetPanel.add(p); JLabel inputLabel = new JLabel(); p.add(inputLabel); inputLabel.setText(name); inputLabel.setAlignmentX(RIGHT_ALIGNMENT); JSlider js = new JSlider(JSlider.HORIZONTAL, 0, 9, initialValue); js.setName(name); js.addChangeListener(this); js.setMajorTickSpacing(1); js.setMinorTickSpacing(1); js.setPaintTicks(true); js.setPaintLabels(true); js.setSnapToTicks(true); p.add(js); } /** * Sets the sizes based on the current world. * @parm boolean [][] world is the description of the world to be displayed */ public void setSizes(boolean [][] world) { Dimension puzzleDrawingSize = new Dimension (world[0].length*squareSize + borderSize*2, world.length*squareSize + borderSize*2); setMinimumSize(puzzleDrawingSize); setPreferredSize(puzzleDrawingSize); setMaximumSize(puzzleDrawingSize); revalidate(); theFrame.pack(); } /** * Draws the painted portions when requested. * @param Graphics gc the graphics context in which to draw */ public void paintComponent(Graphics gc) { if (isOpaque()) { //paint background gc.setColor(getBackground()); gc.fillRect(0, 0, getWidth(), getHeight()); } if (myLife == null) { return; } gc.translate(borderSize, borderSize); boolean [][] world = myLife.world(); Color liveColor = new Color(87, 207, 40); Color deadColor = new Color(240, 240, 240); for (int i = 0; i < world.length; i++) { for (int j = 0; j < world[0].length; j++) { if (world[i][j]) { gc.setColor(liveColor); } else { gc.setColor(deadColor); } gc.fillRect(j*squareSize, i*squareSize, squareSize, squareSize); } } gc.translate(-borderSize, -borderSize); } /* * (non-Javadoc) * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent) */ public void stateChanged(ChangeEvent ce) { JSlider source = (JSlider)ce.getSource(); if (!source.getValueIsAdjusting()) { int value = (int)source.getValue(); if (source.getName().equals(birthMinSliderName)) { birthMin = value; } else if (source.getName().equals(birthMaxSliderName)) { birthMax = value; } else if (source.getName().equals(liveMinSliderName)) { liveMin = value; } else if (source.getName().equals(liveMaxSliderName)) { liveMax = value; } else { System.out.println("Unknown stateChange source"); } } } /** * Perform actions depending which widget was selected. * Determines which sort of widget was selected and bases action on its name. * Currently has actions for the find word button and the open puzzle menu. * @param se the selection event including the source of the event */ public void actionPerformed(ActionEvent se) { String command = se.getActionCommand(); if (se.getSource() == myTimer) { if (myLife != null) { myLife.update(); repaint(); } } else if (command.equals(runButtonText)) { myLife = new Life(System.currentTimeMillis(), 100, 100, birthMin, birthMax, liveMin, liveMax); setSizes(myLife.world()); repaint(); } else { System.out.println("Unknown action: " + command); } } /** * Creates (if necessary) and returns the singleton instance * @return the singleton shared instance */ public static Graphical sharedInstance() { if (sharedApp == null) { sharedApp = new Graphical(); } return sharedApp; } /** * Starts the graphical interface * @param args ignored */ public static void main(String[] args) { javax.swing.SwingUtilities.invokeLater( new Runnable() { public void run() { sharedInstance(); } } ); } }