
/* 
   This applet scrolls the message "hello world (for absolutoely the last time)!" across
   the screen.
*/

import java.awt.*;
import java.applet.Applet;

public class ScrollingHelloWorld extends Applet implements  Runnable {

   Thread runner;  // a thread that is responsible for cycling the message colors.
   
   private final static int GO = 0,           // Constants for use as value of status.
                            SUSPEND = 1,
                            TERMINATE = 2;
                            
   private volatile int status ;   // This variable is used for communication between
                                      // the applet and the thread.  The value is set by
                                      // the applet to tell the thread what to do.
                                      
   String message = "Hello World (for absolutely the last time)!";  // message to be scrolled

   
   int messagePosition = -1;   // Current position of the left end of the message, given as the
                               //   distance in pixels from the left edge of the applet.
                               //   In each frame, this is incremented by one character width
                               //   until the message has scrolled completely off the applet.
                               //   Then it is reset to zero.  The value of -1 indicates that
                               //   the scrolling has not yet begun.
   
   Font messageFont;   // Font used to display the message.
   
   int messageHeight;  // Data about size of message and of a single character.
   int messageWidth;
   int charWidth;
   

   public void init() {
      setBackground(Color.white);
      messageFont = new Font("Monospaced", Font.BOLD, 30);
      FontMetrics fm = getFontMetrics(messageFont);
      messageWidth = fm.stringWidth(message);
      messageHeight = fm.getAscent();
      charWidth = fm.charWidth('H');
   }
   

   synchronized public void paint(Graphics g) {
         // Draw the current frame.
      if (messagePosition > 0) {  // (Otherwise, no frame has yet been computed.)
         g.setColor(Color.red);
         g.setFont(messageFont);
         g.drawString(message, getSize().width - messagePosition, getSize().height/2 + messageHeight/2);
      }
   }
      

   synchronized public void start() {
         // Called when the applet is being started or restarted.
         // Create a new thread or restart the existing thread.
      status = GO;
      if (runner == null || ! runner.isAlive()) {  // Thread doens't yet exist or has died for some reason.
         runner = new Thread(this);
         runner.start();
      }
      else
         notify();
   }
   

   synchronized public void stop() {
         // Called when the applet is about to be stopped.
         // Suspend the thread.
      status = SUSPEND;
      notify();
   }
   

   synchronized public void destroy() {
         // Called when the applet is about to be permanently destroyed;
         // Stop the thread.
      status = TERMINATE;
      notify();
   }  
   
   
   synchronized void nextFrame() {
          // Compute and display the next frame of the animation.  Called by run().
       messagePosition += charWidth;
       if (getSize().width - messagePosition + messageWidth < 0)  // message has moved off left edge
          messagePosition = 0;
       repaint();
   }       
      

   public void run() {
          // The run method scrolls the image while the value of status is GO,
          // and it ends when the value of status becomes TERMINATE.  If the
          // status is SUSPEND, the thread will pause until the notify()
          // method is called by another thread.
      while (status != TERMINATE) {
         synchronized(this) {
            while (status == SUSPEND)
               waitDelay();
         }
         if (status == GO)
            nextFrame();
         if (status == GO)
            waitDelay(250);
      }
   } // end run()
   
   
   synchronized void waitDelay(int milliseconds) {
         // Pause for the specified number of milliseconds
         // OR until the notify() method is called by some other thread.
      try {
         wait(milliseconds);
      }
      catch (InterruptedException e) {
      }
   }
      
   synchronized void waitDelay() {
         // Pause until the notify() method is called by some other thread.
      try {
         wait();
      }
      catch (InterruptedException e) {
      }
   }
      
   
} // end class ScrollingHelloWorld


