////////////////////////////////////////////////////////////////////// // // Grad.java // version 1.0 // // This applet displays a simple timer indicating the amount of // time until or since a specified moment in time. // // The concept behind this program originated as a simple C program // to countdown the days, hours, minutes, and seconds until the // moment of my high school graduation in 1993. // // This program is in the public domain. // // David Simmons // March 12, 2000 // ////////////////////////////////////////////////////////////////////// import java.applet.Applet; import java.awt.*; import java.util.*; public class Grad extends Applet implements Runnable { // debug? static final boolean DEBUG = false; // some constants for calculating time static final int SECS_IN_MIN = 60; static final int SECS_IN_HR = SECS_IN_MIN * 60; static final int SECS_IN_DAY = SECS_IN_HR * 24; // how many milliseconds to sleep between updates static final int SLEEP_TIME = 1000; // Grad control thread Thread thread; // applet display parameters String output1; String output2; int width; int height; // target time, in milliseconds long target_ms = 0; // these strings will contain the text messages to // be displayed around the timer String before = ""; String after = ""; // double-buffering Image offScreenImage; Graphics offScreenGC; Color backgroundColor; Color foregroundColor; // Grad() constructor // // The constructor currently does nothing. public Grad() { } // start() -- overridden Applet method // // start the thread when the applet receives a start(). public void start() { (thread = new Thread(this)).start(); } // stop() -- overridden Applet method // // stop the thread (get rid of the thread reference) when the // applet receives a stop(). public void stop() { thread = null; } // init() -- overridden Applet method // // The init() method is called by the AWT to do some setup and // initialization of variables. public void init() { // Get the dimensions of the applet window Dimension dimension = getSize(); width = dimension.width; height = dimension.height; // Get the "bgcolor" parameter String bgcolor = getParameter("bgcolor"); if (bgcolor != null) { if (bgcolor.charAt(0) == '#') { bgcolor = bgcolor.substring(1); } backgroundColor = new Color(Integer.parseInt(bgcolor, 16)); setBackground(backgroundColor); } else { backgroundColor = new Color(0xFFFFFF); } // Get the "fgcolor" parameter String fgcolor = getParameter("fgcolor"); if(fgcolor != null) { if (fgcolor.charAt(0) == '#') { fgcolor = fgcolor.substring(1); } foregroundColor = new Color(Integer.parseInt(fgcolor, 16)); setForeground(foregroundColor); } else { foregroundColor = new Color(0x000000); } // Get the "target" parameter, which contains the // target time in the format "YYYYMMDDhhmm", and convert // it to an absolute measurement of time in milliseconds. String target_string = getParameter("target"); if (target_string != null) { int year = Integer.parseInt(target_string.substring(0, 4)); int month = Integer.parseInt(target_string.substring(4, 6)) - 1; int day = Integer.parseInt(target_string.substring(6, 8)); int hour = Integer.parseInt(target_string.substring(8, 10)); int minute = Integer.parseInt(target_string.substring(10, 12)); int seconds = 0; GregorianCalendar gregoriancalendar = new GregorianCalendar(); gregoriancalendar.setTimeZone(TimeZone.getTimeZone("GMT")); gregoriancalendar.set(year, month, day, hour, minute, seconds); target_ms = gregoriancalendar.getTime().getTime(); } else { // default to new year's 2001 if no target specified GregorianCalendar gregoriancalendar = new GregorianCalendar(); gregoriancalendar.setTimeZone(TimeZone.getTimeZone("GMT")); gregoriancalendar.set(2001, 0, 1, 0, 0, 0); target_ms = gregoriancalendar.getTime().getTime(); } if (DEBUG) { System.out.println("target_ms is " + target_ms); } // Get the "stringXX" parameters which contain the // text to display after the timer. if (getParameter("before") != null) { before = getParameter("before"); } if (getParameter("after") != null) { after = getParameter("after"); } } // update() -- overriden Component method // // The update() method is called by the AWT when a repaint // is requested. This method is responsible for setting up // an offscreen image buffer for paint() to draw into, calling // paint(), and blatting the new image to the screen. public void update(Graphics g) { // create an offscreen image for double-buffering if (offScreenImage == null) { offScreenImage = createImage(width, height); offScreenGC = offScreenImage.getGraphics(); } // clear the offscreen image offScreenGC.setColor(backgroundColor); offScreenGC.fillRect(0,0,width,height); offScreenGC.setColor(foregroundColor); // have the paint() method draw the new text into the // offscreen image buffer paint(offScreenGC); // blat the offscreen image to the screen g.drawImage(offScreenImage,0,0,this); } // paint() -- overriden Component method // // paint() is called by update() with the graphics context // set to an offscreen image buffer. update(), in turn, is // called by the AWT whenever a repaint() is requested. public void paint(Graphics g) { // these variables will hold the x and y coordinates of // each of the two strings to be displayed. int x1,y1,x2,y2; // get a FontMetrics object which can be used to determine the // dimensions of the strings with the font in use. Font font = g.getFont(); FontMetrics fontmetrics = getFontMetrics(font); // calculate the y coordinates for each string, based on the // y dimensions of the font. y1 = fontmetrics.getMaxAscent(); y2 = y1*2 + fontmetrics.getMaxDescent() + fontmetrics.getLeading(); // these are the lengths (in characters) of each string. int length1 = output1.length(); int length2 = output2.length(); // create a character-array for each string, since // the Fontmetrics.charsWidth method wants to examine a // character-array instead of a String object. char ca1[] = new char[length1]; char ca2[] = new char[length2]; output1.getChars(0, length1, ca1, 0); output2.getChars(0, length2, ca2, 0); // calculate the pixel width of each string. int cw1 = fontmetrics.charsWidth(ca1, 0, length1); int cw2 = fontmetrics.charsWidth(ca2, 0, length2); // calculate the x coordinates for each string, centering // each string within the applet window. x1 = width / 2 - cw1 / 2; x2 = width / 2 - cw2 / 2; // finally, draw each string into the graphics context. g.drawString(output1, x1, y1); g.drawString(output2, x2, y2); } // run() -- required for the Runnable interface // // This is the heart of the applet. The applet's start() method // creates a thread which calls this run() method via the applet's // Runnable interface. run() will enter an infinite loop (until the // applet's stop() is called) which creates the strings to be displayed, // draws them, and sleeps one second. public void run() { boolean flag = false; try { // does "thread" still represent the current thread? then continue... for (; thread == Thread.currentThread(); Thread.sleep(SLEEP_TIME)) { // near-infinite loop, pauses for SLEEP_TIME every iteration // calculate the elapsed seconds, and if this is the seconds // "since" or the seconds "until". int elapsed = (int)((System.currentTimeMillis() - target_ms) / 1000L); if (elapsed < 0) { flag = true; elapsed = -elapsed; } // calculate the days, hours, minutes int days = elapsed / SECS_IN_DAY; elapsed -= days * SECS_IN_DAY; int hours = elapsed / 3600; elapsed -= hours * SECS_IN_HR; int minutes = elapsed / 60; elapsed -= minutes * SECS_IN_MIN; int seconds = elapsed; // formulate the first string which contains the // text of the countdown output1 = days + " day" + (days == 1 ? "" : "s") + " " + hours + " hour" + (hours == 1 ? "" : "s") + " " + minutes + " minute" + (minutes == 1 ? "" : "s") + " " + seconds + " second" + (seconds == 1 ? "" : "s"); // formulate the second string which contains the // informative text supplied by the user if (flag) { output2 = before; } else { output2 = after; } // ask AWT for a repaint repaint(); } } catch(Exception e) { } } }