
/*
    Applet scrolls a message, while a red oval grows from size zero
    to the full size of the applet.  Two red bars across the applet
    lie above and below the path of the message.  This is an example
    of animation using an off-screen canvas.
    
    David Eck, September 2, 1996
*/


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

public class ScrollingMessage extends Applet
                              implements Runnable {
   
   String message = "Hello World!";   // can be modified by an applet param with name "message"
   
   int message_x, message_y;   // position where message is drawn
   int min_x, max_x;           // largest and smallest values for message_x
   int bar1_y, bar2_y;         // y coordinates for red lines across the applet
   
   int pulse_frames = 30;   // number of frames it takes for red oval to grow to full size
   int scroll_frames = 100; // number of framed it takes for message to scroll across the screen
         
   Thread runner = null;   // Thread for running the animation
   int sleepTime = 100;    // Pause between frames, in milliseconds
   
   Image OSC = null;       // the off-screen image
   Graphics OSC_graphics;  // graphics context for OSC 
   int width = -1;         // width of OSC  (value of -1 indicates it doesn't exist)
   int height = -1;        // height of OSC
   

   public void init() {
         // just check for the parameter
      String param = getParameter("message");
      if (param != null)
         message = param;
   }
       
   synchronized public void paint(Graphics g) {
      if (OSC != null)  // if off-screen image exists, copy it to screen
          g.drawImage(OSC,0,0,this);
      else {  // otherwise, fill the applet with the backgroudn color
          g.setColor(getBackground());
          g.fillRect(0,0,size().width,size().height);
      }
   }
       
   public void update(Graphics g) {
         // redefine update() so that it doesn't erase the applet;
         // this will avoid flickering when the applet is redrawn
      paint(g);
   }
         
   public void start() {
      if (runner == null) {  // create and start the thread
         runner = new Thread(this);
         runner.start();
      }
      else if (runner.isAlive()) {  // resume the thread,
         runner.resume();           //    unless it is already dead
      }
   }
         
   public void stop() {
      if (runner.isAlive())  // suspend the thread, 
         runner.suspend();   //     if it is still alive
   }
         
   public void destroy() {
      if (runner.isAlive())  // if the thread is still running,
          runner.stop();     //     then stop it
      runner = null;
   }
   
   synchronized void doResize(int w, int h) {
         // Resize the off-screen canvas to width w, height h
         // (presumably the new size of the applet).  This is
         // called only by drawNextFrame().  The initial values
         // of instance variables width and height force this
         // to be called before any frames are drawn.
       OSC = null;
       width = w;
       height = h;
       OSC = createImage(width,height);  // create off-screen canvas
       OSC_graphics = OSC.getGraphics();
       Font font = getFont();  // font for drawing message, about 1/8 height of applet
       font = new Font("TimesRoman", Font.PLAIN, height/8);
       OSC_graphics.setFont(font);
       FontMetrics fm = getFontMetrics(font);
           // compute drawing parameters that depend on applet and font size
       message_y = (height - fm.getHeight()) / 2 + fm.getAscent() + fm.getLeading() / 2;
       bar1_y = message_y - fm.getAscent() - fm.getLeading();
       bar2_y = message_y + fm.getLeading() + fm.getDescent();
       min_x = -fm.stringWidth(message);
       max_x = width;
   }
         
   
   synchronized void drawNextFrame(int scroll_frame, int pulse_frame) {
           
           // Create one frame in the animation.  scroll_frame gives
           // relative position of scrolling message; pulse_frame
           // gives relative size of growing oval.
           
       int w = size().width;   // check if size of applet has changed
       int h = size().height;
       if (w != width || h != height)
          doResize(w,h);
       
           // compute drawing parameters that depend on which frame this is
       message_x = max_x - ((scroll_frame * (max_x-min_x)) / scroll_frames);
       int a = (width/2) - (pulse_frame*width)/(2*pulse_frames);   // left edge for oval
       int b = (height/2) - (pulse_frame*height)/(2*pulse_frames); // top edge for oval
       
       OSC_graphics.setColor(Color.white);                 // fill the background
       OSC_graphics.fillRect(0,0,width,height);
       OSC_graphics.setColor(Color.red);                   // draw the oval
       OSC_graphics.fillOval(a,b,width-2*a,height-2*b);
       OSC_graphics.drawLine(0,bar1_y,width,bar1_y);       // draw the bars (2 pixels high)
       OSC_graphics.drawLine(0,bar1_y+1,width,bar1_y+1);
       OSC_graphics.drawLine(0,bar2_y,width,bar2_y);
       OSC_graphics.drawLine(0,bar2_y-1,width,bar2_y-1);
       OSC_graphics.setColor(Color.black);                 // draw the message
       OSC_graphics.drawString(message,message_x,message_y);
   
   }
       
   public void run() {
         // subroutine to be executed by the thread
      int scroll_frame = 0;  // frame number for scrolling
      int pulse_frame = 0;   // frame number for growing oval
      while (true) {
         drawNextFrame(scroll_frame,pulse_frame);
         repaint();
         scroll_frame++;
         if (scroll_frame > scroll_frames)
            scroll_frame = 0;
         pulse_frame++;
         if (pulse_frame > pulse_frames)
           pulse_frame = 0;
         try { Thread.sleep(sleepTime); }
         catch (InterruptedException e) { }
      }
   }
         
}  // end of class ScrollingMessage
