// Class ClickerController is the controller for this application. The idea // is that the computer and the user alternate turns, clicking where circles // should appear. This class constructs a single view that has a mouse // listener. Obviously the computer doesn't actually click anywhere, so this // class simulates random computer moves when appropriate, including a short // delay to make it seem like the computer had to decide where to click. import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; import java.io.*; import javax.swing.Timer; public class ClickerController { private JFrame frame; // top-level application frame private ClickerModel model; // the model private ClickerPanel panel; // the view private boolean userFirst; // is the user supposed to go first? private Random random; // used to make random computer moves private Timer timer; // used to delay the computer move private boolean clickOkay; // whether we are accepting user clicks private Action save; // save current state private Action restore; // restore from previous save private JPopupMenu pop; // popup menu // Constructs a controller for the given model, with the second argument // indicating whether or not the user should go first. public ClickerController(ClickerModel model, boolean userFirst) { this.model = model; frame = new JFrame("CSE190L Clicker"); frame.setSize(600, 400); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // construct the view, add it to the frame, register it with the model, // set up its one control (a mouse listener) panel = new ClickerPanel(model); frame.add(panel, BorderLayout.CENTER); model.addListener(panel); panel.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { handleClick(e); } }); random = new Random(); this.userFirst = userFirst; timer = new Timer(2000, new ActionListener() { public void actionPerformed(ActionEvent e) { computerMove(); clickOkay = true; } }); timer.setRepeats(false); addControls(); } // launch the program, asking the computer for a move if it is first public void run() { frame.setVisible(true); if (!userFirst) computerMove(); clickOkay = true; pop.show(panel, 50, 50); pop.repaint(); } // Handle a mouse click from the user. The user might have clicked while // the computer was making a move. Such clicks should be ignored. We // accomplish this by keeping a flag indicating whether user clicks are // okay. If not, we ignore them. private void handleClick(MouseEvent e) { // ignore user clicks that happened while the computer was moving if (!clickOkay) return; save.setEnabled(true); model.add(e.getPoint(), Player.HUMAN); clickOkay = false; timer.start(); } // Makes a random computer move, "clicking" on a random position that will // lead to a circle that is within the bounds of the view. private void computerMove() { int x = ClickerPanel.RADIUS + random.nextInt(panel.getWidth() - 2 * ClickerPanel.RADIUS); int y = ClickerPanel.RADIUS + random.nextInt(panel.getHeight() - 2 * ClickerPanel.RADIUS); model.add(new Point(x, y), Player.COMPUTER); } private void addControls() { // initialize buttons and actions save = new SaveAction(); restore = new RestoreAction(); restore.setEnabled(new File("clicker.dat").exists()); // construct a menu JMenuBar bar = new JMenuBar(); JMenu menu = new JMenu("File"); menu.add(save); menu.add(restore); bar.add(menu); frame.setJMenuBar(bar); // construct a panel JPanel p = new JPanel(); p.add(new JButton(save)); p.add(new JButton(restore)); frame.add(p, BorderLayout.SOUTH); // construct a toolbar JToolBar tbar = new JToolBar(); tbar.add(save); tbar.add(restore); frame.add(tbar, BorderLayout.NORTH); // construct a popup menu pop = new JPopupMenu(); pop.add(save); pop.add(restore); panel.setComponentPopupMenu(pop); } private class SaveAction extends AbstractAction { public SaveAction() { super("Save"); putValue(Action.SMALL_ICON, new ImageIcon("save.gif")); putValue(Action.MNEMONIC_KEY, new Integer('S')); putValue(Action.SHORT_DESCRIPTION, "save current points"); putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK)); } public void actionPerformed(ActionEvent e) { try { FileOutputStream fos = new FileOutputStream("clicker.dat"); ObjectOutputStream output = new ObjectOutputStream(fos); output.writeObject(model); } catch (IOException error) { throw new RuntimeException(error.toString()); } restore.setEnabled(true); setEnabled(false); } } private class RestoreAction extends AbstractAction { public RestoreAction() { super("Restore"); putValue(Action.SMALL_ICON, new ImageIcon("open.gif")); putValue(Action.MNEMONIC_KEY, new Integer('R')); putValue(Action.SHORT_DESCRIPTION, "restore previous points"); putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_MASK)); } public void actionPerformed(ActionEvent e) { timer.stop(); try { FileInputStream fis = new FileInputStream("clicker.dat"); ObjectInputStream input = new ObjectInputStream(fis); model = (ClickerModel) input.readObject(); } catch (Exception error) { throw new RuntimeException(error.toString()); } panel.update(model); model.addListener(panel); clickOkay = true; } } }
Stuart Reges
Last modified: Fri May 18 10:10:24 PDT 2007