//: Stopwatch.java // // (By the way, the applet tag is deprecated in HTML 4 in favor of object) import java.util.*; import java.awt.*; import java.awt.event.*; import java.applet.*; /** A multithreaded GUI stopwatch. * Can be used as both an applet and an application. * @version CS2136 - Term D'00 - Assignment 5 * @author Peter Cooper Jr. */ public class Stopwatch extends Applet { // All the components that need variable names private final Panel pnlTop = new Panel(); private final Panel pnlBot = new Panel(); private final Label lblDate = new Label(); private final Label lblTime = new Label(); private final Label lblWatch = new Label(); private final Button btnGo = new Button("Start"); private final Button btnReset = new Button("Reset"); private final Label lblSplit = new Label(); private final Button btnSplit = new Button("Split"); private final Button btnSplitReset = new Button("Split Reset"); private final Button btnLapAdd = new Button("New Lap"); private final Button btnLapReset = new Button("Lap Reset"); private final java.awt.List lstLaps = new java.awt.List(); // My two daemon threads private final UpdateClockThread ucThread = new UpdateClockThread(); private final StopwatchThread swThread = new StopwatchThread(); /** Listens to the Start/Stop/Resume button. * @version CS2136 - Term D'00 - Assignment 5 * @author Peter Cooper Jr. */ private class btnGoListener implements ActionListener { /** Actually run when the button gets clicked. *@param e The event */ public void actionPerformed(ActionEvent e) { if ((btnGo.getLabel().equals("Start")) || (btnGo.getLabel().equals("Resume"))) { // Start the clock! swThread.go(); btnGo.setLabel("Stop"); btnGo.setBackground(Color.red); } else if (btnGo.getLabel().equals("Stop")) { // Stop the clock! swThread.noGo(); btnGo.setLabel("Resume"); btnGo.setBackground(Color.green); } } } /** Listens to the Reset button. * @version CS2136 - Term D'00 - Assignment 5 * @author Peter Cooper Jr. */ private class btnResetListener implements ActionListener { /** Actually run when the button gets clicked. *@param e The event */ public void actionPerformed(ActionEvent e) { swThread.reset(); btnGo.setLabel("Start"); btnGo.setBackground(Color.green); } } /** Listens to the Split button. * @version CS2136 - Term D'00 - Assignment 5 * @author Peter Cooper Jr. */ private class btnSplitListener implements ActionListener { /** Actually run when the button gets clicked. *@param e The event */ public void actionPerformed(ActionEvent e) { lblSplit.setText(lblWatch.getText()); } } /** Listens to the Split Reset button. * @version CS2136 - Term D'00 - Assignment 5 * @author Peter Cooper Jr. */ private class btnSplitResetListener implements ActionListener { /** Actually run when the button gets clicked. *@param e The event */ public void actionPerformed(ActionEvent e) { lblSplit.setText(""); } } /** Listens to the Lap Add button. * @version CS2136 - Term D'00 - Assignment 5 * @author Peter Cooper Jr. */ private class btnLapAddListener implements ActionListener { /** Actually run when the button gets clicked. *@param e The event */ public void actionPerformed(ActionEvent e) { swThread.addLap(); } } /** Listens to the Lap Reset button. * @version CS2136 - Term D'00 - Assignment 5 * @author Peter Cooper Jr. */ private class btnLapResetListener implements ActionListener { /** Actually run when the button gets clicked. *@param e The event */ public void actionPerformed(ActionEvent e) { swThread.resetLap(); } } /** A thread that updates the current date & time. * @version CS2136 - Term D'00 - Assignment 5 * @author Peter Cooper Jr. */ private class UpdateClockThread extends Thread { /** The actual work of the thread. */ public void run() { while (true) { Calendar now = Calendar.getInstance(); String month = Integer.toString(now.get(Calendar.MONTH)+1); String date = Integer.toString(now.get(Calendar.DAY_OF_MONTH)); String year = Integer.toString(now.get(Calendar.YEAR)); String hour = Integer.toString(now.get(Calendar.HOUR)); if (hour.equals("0")) hour = "12"; String minute = Integer.toString(now.get(Calendar.MINUTE)); if (minute.length() == 1) minute = "0" + minute; String second = Integer.toString(now.get(Calendar.SECOND)); if (second.length() == 1) second = "0" + second; String ampm = now.get(Calendar.AM_PM) == Calendar.AM ? "AM" : "PM"; lblDate.setText(month + "/" + date + "/" + year); lblTime.setText(hour + ":" + minute + ":" + second + " " + ampm); try { sleep(500); } catch (InterruptedException e) {} } } } /** A thread that keeps track of the stopwatch & updates * the display accordingly. * @version CS2136 - Term D'00 - Assignment 5 * @author Peter Cooper Jr. */ private class StopwatchThread extends Thread { /** Whether or not stopwatch is running. */ private boolean going = false; /** Stores elapsed milliseconds of previous runs. */ private long prevElapsed = 0; /** Stores beginning time of this run. */ private Date startDate = new Date(); /** Current lap number. */ private int lapNum = 0; /** Elapsed time at end of last lap. */ private long lastLapTime = 0; /** Returns elapsed time in milliseconds. *@return The elapsed time */ private long elapsedTime() { return prevElapsed + (going ? new Date().getTime() - startDate.getTime() : 0); } /** Changes the number of elapsed milliseconds into a string. *@param time Number of elapsed milliseconds *@return The elapsed time as a string. */ private String msToString(long time) { String ms, sec, min; if (time % 10 >= 5) //round to nearest hundredth time += 5; ms = Long.toString(time % 1000); while (ms.length() < 3) ms = "0" + ms; ms = ms.substring(0, ms.length() - 1); time /= 1000; sec = Long.toString(time % 60); if (sec.length() == 1) sec = "0" + sec; time /= 60; min = Long.toString(time); return min + ":" + sec + "." + ms; } /** Called when the stopwatch is to go. */ public void go() { startDate = new Date(); going = true; } /** Called when the stopwatch is to stop. */ public void noGo() { prevElapsed = elapsedTime(); going = false; } /** Resets the stopwatch. */ public void reset() { going = false; prevElapsed = 0; lastLapTime = 0; } /** Adds a lap to the list. */ public void addLap() { long elapsed = elapsedTime(); lstLaps.add("Lap " + Integer.toString(++lapNum)+ " -- " + "Elapsed Time: " + msToString(elapsed) + " -- " + "Lap Time: " + msToString(elapsed - lastLapTime)); lastLapTime = elapsed; } /** Resets the lap list. */ public void resetLap() { lstLaps.removeAll(); lapNum = 0; lastLapTime = 0; } /** Main code of the thread. */ public void run() { while (true) { lblWatch.setText(msToString(elapsedTime())); yield(); } } } /** Initializes the applet by adding its components, registering button * listeners, and starting some threads. */ public void init() { setLayout(new GridLayout(2,1)); setBackground(Color.lightGray); setForeground(Color.black); pnlTop.setLayout(new GridLayout(4,4)); pnlTop.add(new Label("Date:")); pnlTop.add(lblDate); pnlTop.add(new Label("Time:")); pnlTop.add(lblTime); pnlTop.add(new Label("Stopwatch:")); //lblWatch.setBackground(Color.black); lblWatch.setForeground(Color.blue); pnlTop.add(lblWatch); pnlTop.add(btnGo); btnGo.setBackground(Color.green); pnlTop.add(btnReset); pnlTop.add(new Label("Split:")); pnlTop.add(lblSplit); pnlTop.add(btnSplit); pnlTop.add(btnSplitReset); pnlTop.add(new Label("Laps:")); pnlTop.add(new Label()); pnlTop.add(btnLapAdd); pnlTop.add(btnLapReset); pnlBot.setLayout(new GridLayout(1,1)); pnlBot.add(lstLaps); add(pnlTop); add(pnlBot); btnGo.addActionListener(new btnGoListener()); btnReset.addActionListener(new btnResetListener()); btnSplit.addActionListener(new btnSplitListener()); btnSplitReset.addActionListener(new btnSplitResetListener()); btnLapAdd.addActionListener(new btnLapAddListener()); btnLapReset.addActionListener(new btnLapResetListener()); swThread.setDaemon(true); ucThread.setDaemon(true); swThread.start(); ucThread.start(); } /** A main for the application. * @param args An array of command-line parameters that are ignored */ public static void main(String[] args) { Stopwatch applet = new Stopwatch(); Frame aFrame = new Frame("Stopwatch"); aFrame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); aFrame.add(applet, BorderLayout.CENTER); aFrame.setSize(400, 200); applet.init(); applet.start(); aFrame.setVisible(true); } }