Drop_of_Rain
asked on
general purpose animator
I got this general purpose animator from sun. I want to know if this could be used to do this:
I want to be able to have 4 items in a row, move one of them into the background getting smaller and moving one item at a time into the background as well, lets say from right to left. Then be able to bring them back to the foregroung one at a time in the opposite direction. If so what would have to be done. I know this is a big question. Answer the first part of the question, and if so I will break the question up into any questions.
import java.awt.*;
import java.awt.event.*;
import java.applet.Applet;
import java.applet.AudioClip;
import java.util.Vector;
import java.util.Hashtable;
import java.util.Enumeration;
import java.net.URL;
import java.net.MalformedURLExcep tion;
/**
 * An applet that plays a sequence of images, as a loop or a one-shot.
 * Can have a soundtrack and/or sound effects tied to individual frames.
 * See the <a href="http://java.sun.com/applets/applets/Animator/">Animator
 * home page</a> for details and updates.
 *
 * @author Herb Jellinek
 * @version 1.10, 02/05/97
 */
public class Animator extends Applet
           implements Runnable,
                       MouseListener {
 Â
  /**
   * The images, in display order (Images).
   */
  Vector images = null;
  /**
   * A table that maps images to image names - for error messages.
   */
  Hashtable imageNames = new Hashtable(10);
  /**
   * Duration of each image (Integers, in milliseconds).
   */
  Hashtable durations = null;
  /**
   * Sound effects for each image (AudioClips).
   */
  Hashtable sounds = null;
  /**
   * Position of each image (Points).
   */
  Hashtable positions = null;
  /**
   * MediaTracker 'class' ID numbers.
   */
  static final int STARTUP_ID   = 0;
  static final int BACKGROUND_ID = 1;
  static final int ANIMATION_ID  = 2;
  /**
   * Start-up image URL, if any.
   */
  URL startUpImageURL = null;
  /**
   * Start-up image, if any.
   */
  Image startUpImage = null;
  /**
   * Background image URL, if any.
   */
  URL backgroundImageURL = null;
  /**
   * Background image, if any.
   */
  Image backgroundImage = null;
  /**
   * Background color, if any.
   */
  Color backgroundColor = null;
 Â
  /**
   * The soundtrack's URL.
   */
  URL soundtrackURL = null;
  /**
   * The soundtrack.
   */
  AudioClip soundtrack = null;
  /**
   * URL to link to, if any.
   */
  URL hrefURL = null;
  /**
   * Frame target for that URL, if any.
   */
  String hrefTarget = null;
  /**
   * Our width.
   */
  int appWidth = 0;
  /**
   * Our height.
   */
  int appHeight = 0;
  /**
   * The directory or URL from which the images are loaded
   */
  URL imageSource = null;
  /**
   * The directory or URL from which the sounds are loaded
   */
  URL soundSource = null;
  /**
   * The thread animating the images.
   */
  Thread engine = null;
  /**
   * The current loop slot - index into 'images.'
   */
  int frameNum;
  /**
   * frameNum as an Object - suitable for use as a Hashtable key.
   */
  Integer frameNumKey;
 Â
  /**
   * The current X position (for painting).
   */
  int xPos = 0;
 Â
  /**
   * The current Y position (for painting).
   */
  int yPos = 0;
 Â
  /**
   * The default number of milliseconds to wait between frames.
   */
  public static final int defaultPause = 3900;
 Â
  /**
   * The global delay between images, which can be overridden by
   * the PAUSE parameter.
   */
  int globalPause = defaultPause;
  /**
   * Whether or not the thread has been paused by the user.
   */
  boolean userPause = false;
  /**
   * Repeat the animation?  If false, just play it once.
   */
  boolean repeat;
  /**
   * The offscreen image, used in double buffering
   */
  Image offScrImage;
  /**
   * The offscreen graphics context, used in double buffering
   */
  Graphics offScrGC;
  /**
   * The MediaTracker we use to load our images.
   */
  MediaTracker tracker;
 Â
  /**
   * Can we paint yet?
   */
  boolean loaded = false;
  /**
   * Was there an initialization error?
   */
  boolean error = false;
  /**
   * What we call an image file in messages.
   */
  static final String imageLabel = "image";
 Â
  /**
   * What we call a sound file in messages.
   */
  static final String soundLabel = "sound";
 Â
  /**
   * Print silly debugging info?
   */
  static final boolean debug = false;
  /**
   * Where to find the source code and documentation - for informational
   * purposes.
   */
  static final String sourceLocation =
          "http://java.sun.com/applets/applets/Animator/";
  /**
   * Instructions to the user: how to produce the popup.
   */
  static final String userInstructions = "shift-click for errors, info";
  /**
   * Applet info.
   */
  public String getAppletInfo() {
      return "Animator v1.10 (02/05/97), by Herb Jellinek";
  }
  /**
   * Parameter info.
   */
  public String[][] getParameterInfo() {
      String[][] info = {
        {"imagesource",       "URL",            "a directory"},
        {"startup",       "URL",            "image displayed at start-up"},
        {"backgroundcolor", "int",      "color (24-bit RGB number) displayed as background"},
        {"background",       "URL",            "image displayed as background"},
        {"startimage",       "int",            "index of first image"},
        {"endimage",       "int",            "index of last image"},
        {"namepattern",   "URL",      "generates indexed names"},
        {"images",      "URLs",     "list of image indices"},
        {"href",            "URL",            "page to visit on mouse-click"},
        {"target",            "name",            "frame to put that page in"},
        {"pause",           "int",            "global pause, milliseconds"},
        {"pauses",           "ints",       "individual pauses, milliseconds"},
        {"repeat",           "boolean",       "repeat? true or false"},
        {"positions",      "coordinates",       "path images will follow"},
        {"soundsource",      "URL",            "audio directory"},
        {"soundtrack",      "URL",            "background music"},
        {"sounds",            "URLs",            "list of audio samples"},
      };
      return info;
  }
  /**
   * Show a crude "About" box.  Displays credits, errors (if any), and
   * parameter values and documentation.
   */
  void showDescription() {
      DescriptionFrame description = new DescriptionFrame();
       Â
      description.tell("\t\t"+ge tAppletInf o()+"\n");
      description.tell("Updates, documentation at "+sourceLocation+"\n\n");
      description.tell("Document base: "+getDocumentBase()+"\n");
      description.tell("Code base: "+getCodeBase()+"\n\n");
     Â
      Object errors[] = tracker.getErrorsAny();
      if (errors != null) {
        description.tell("Applet image errors:\n");
        for (int i = 0; i < errors.length; i++) {
           if (errors[i] instanceof Image) {
             URL url = (URL)imageNames.get((Image )errors[i] );
             if (url != null) {
                 description.tell(" "+url+" not loaded\n");
             }
           }
        }
        description.tell("\n");
      }
     Â
      if (images == null || images.size() == 0) {
        description.tell("\n** No images loaded **\n\n");
      }
      description.tell("Applet parameters:\n");
      description.tell(" width = "+getParameter("WIDTH")+"\ n");
      description.tell(" height = "+getParameter("HEIGHT")+" \n");
     Â
      String params[][] = getParameterInfo();
      for (int i = 0; i < params.length; i++) {
        String name = params[i][0];
        description.tell(" "+name+" = "+getParameter(name)+
                   "\t ["+params[i][2]+"]\n");
      }
     Â
      description.show();
  }
  /**
   * Print silly debugging info to the standard output.
   */
  void dbg(String s) {
      if (debug) {
        System.out.println("> "+s);
      }
  }
  /**
   * Local version of getParameter for debugging purposes.
   */
  public String getParam(String key) {
      String result = getParameter(key);
      dbg("getParameter("+key+") = "+result);
      return result;
  }
  final int setFrameNum(int newFrameNum) {
      frameNumKey = new Integer(frameNum = newFrameNum);
      return frameNum;
  }
 Â
  /**
   * Parse the IMAGES parameter.  It looks like
   * 1|2|3|4|5, etc., where each number (item) names a source image.
   *
   * @return a Vector of (URL) image file names.
   */
  Vector parseImages(String attr, String pattern)
  throws MalformedURLException {
      if (pattern == null) {
        pattern = "T%N.gif";
      }
      Vector result = new Vector(10);
      for (int i = 0; i < attr.length(); ) {
        int next = attr.indexOf('|', i);
        if (next == -1) next = attr.length();
        String file = attr.substring(i, next);
        result.addElement(new URL(imageSource, doSubst(pattern, file)));
        i = next + 1;
      }
      return result;
  }
  /**
   * Fetch an image and wait for it to come in.  Used to enforce a load
   * order for background and startup images.
   * Places URL and image in imageNames table.
   */
  Image fetchImageAndWait(URL imageURL, int trackerClass)
  throws InterruptedException {
      Image image = getImage(imageURL);
      tracker.addImage(image, trackerClass);
      imageNames.put(image, imageURL);
      tracker.waitForID(trackerC lass);
      return image;
  }
  /**
   * Fetch the images named in the argument.  Is restartable.
   *
   * @param images a Vector of URLs
   * @return true if all went well, false otherwise.
   */
  boolean fetchImages(Vector images) {
      if (images == null) {
        return true;
      }
      int size = images.size();
      for (int i = 0; i < size; i++) {
        Object o = images.elementAt(i);
        if (o instanceof URL) {
           URL url = (URL)o;
           tellLoadingMsg(url, imageLabel);
           Image im = getImage(url);
           tracker.addImage(im, ANIMATION_ID);
           images.setElementAt(im, i);
           imageNames.put(im, url);
        }
      }
      try {
        tracker.waitForID(ANIMATIO N_ID);
      } catch (InterruptedException e) {}
      if (tracker.isErrorID(ANIMATI ON_ID)) {
        return false;
      }
      return true;
  }
  /**
   * Parse the SOUNDS parameter.  It looks like
   * train.au||hello.au||stop.a u, etc., where each item refers to a
   * source image.  Empty items mean that the corresponding image
   * has no associated sound.
   *
   * @return a Hashtable of SoundClips keyed to Integer frame numbers.
   */
  Hashtable parseSounds(String attr, Vector images)
  throws MalformedURLException {
      Hashtable result = new Hashtable();
      int imageNum = 0;
      int numImages = images.size();
      for (int i = 0; i < attr.length(); ) {
        if (imageNum >= numImages) break;
       Â
        int next = attr.indexOf('|', i);
        if (next == -1) next = attr.length();
       Â
        String sound = attr.substring(i, next);
        if (sound.length() != 0) {
           result.put(new Integer(imageNum),
                  new URL(soundSource, sound));
        }
        i = next + 1;
        imageNum++;
      }
      return result;
  }
  /**
   * Fetch the sounds named in the argument.
   * Is restartable.
   *
   * @return URL of the first bogus file we hit, null if OK.
   */
  URL fetchSounds(Hashtable sounds) {
      for (Enumeration e = sounds.keys() ; e.hasMoreElements() ;) {
        Integer num = (Integer)e.nextElement();
        Object o = sounds.get(num);
        if (o instanceof URL) {
           URL file = (URL)o;
           tellLoadingMsg(file, soundLabel);
           try {
             sounds.put(num, getAudioClip(file));
           } catch (Exception ex) {
             return file;
           }
        }
      }
      return null;
  }
  /**
   * Parse the PAUSES parameter.  It looks like
   * 1000|500|||750, etc., where each item corresponds to a
   * source image.  Empty items mean that the corresponding image
   * has no special duration, and should use the global one.
   *
   * @return a Hashtable of Integer pauses keyed to Integer
   * frame numbers.
   */
  Hashtable parseDurations(String attr, Vector images) {
      Hashtable result = new Hashtable();
      int imageNum = 0;
      int numImages = images.size();
      for (int i = 0; i < attr.length(); ) {
        if (imageNum >= numImages) break;
       Â
        int next = attr.indexOf('|', i);
        if (next == -1) next = attr.length();
        if (i != next) {
           int duration = Integer.parseInt(attr.subs tring(i, next));
           result.put(new Integer(imageNum), new Integer(duration));
        } else {
           result.put(new Integer(imageNum),
                  new Integer(globalPause));
        }
        i = next + 1;
        imageNum++;
      }
      return result;
  }
  /**
   * Parse a String of form xxx@yyy and return a Point.
   */
  Point parsePoint(String s) throws ParseException {
      int atPos = s.indexOf('@');
      if (atPos == -1) throw new ParseException("Illegal position: "+s);
      return new Point(Integer.parseInt(s.s ubstring(0 , atPos)),
                 Integer.parseInt(s.substri ng(atPos + 1)));
  }
  /**
   * Parse the POSITIONS parameter.  It looks like
   * 10@30|11@31|||12@20, etc., where each item is an X@Y coordinate
   * corresponding to a source image.  Empty items mean that the
   * corresponding image has the same position as the preceding one.
   *
   * @return a Hashtable of Points keyed to Integer frame numbers.
   */
  Hashtable parsePositions(String param, Vector images)
  throws ParseException {
      Hashtable result = new Hashtable();
      int imageNum = 0;
      int numImages = images.size();
      for (int i = 0; i < param.length(); ) {
        if (imageNum >= numImages) break;
       Â
        int next = param.indexOf('|', i);
        if (next == -1) next = param.length();
        if (i != next) {
           result.put(new Integer(imageNum),
                  parsePoint(param.substring (i, next)));
        }
        i = next + 1;
        imageNum++;
      }
      return result;
  }
 Â
  /**
   * Get the dimensions of an image.
   * @return the image's dimensions.
   */
  Dimension getImageDimensions(Image im) {
      return new Dimension(im.getWidth(null ), im.getHeight(null));
  }
  /**
   * Substitute an integer some number of times in a string, subject to
   * parameter strings embedded in the string.
   * Parameter strings:
   *  %N - substitute the integer as is, with no padding.
   *  %<digit>, for example %5 - substitute the integer left-padded with
   *     zeros to <digits> digits wide.
   *  %% - substitute a '%' here.
   * @param inStr the String to substitute within
   * @param theInt the int to substitute, as a String.
   */
  String doSubst(String inStr, String theInt) {
      String padStr = "0000000000";
      int length = inStr.length();
      StringBuffer result = new StringBuffer(length);
     Â
      for (int i = 0; i < length;) {
        char ch = inStr.charAt(i);
        if (ch == '%') {
           i++;
           if (i == length) {
             result.append(ch);
           } else {
             ch = inStr.charAt(i);
             if (ch == 'N' || ch == 'n') {
                 // just stick in the number, unmolested
                 result.append(theInt);
                 i++;
             } else {
                 int pad;
                 if ((pad = Character.digit(ch, 10)) != -1) {
                   // we've got a width value
                   String numStr = theInt;
                   String scr = padStr+numStr;
                   result.append(scr.substrin g(scr.leng th() - pad));
                   i++;
                 } else {
                   result.append(ch);
                   i++;
                 }
             }
           }
        } else {
           result.append(ch);
           i++;
        }
      }
      return result.toString();
  }     Â
  /**
   * Stuff a range of image names into a Vector.
   *
   * @return a Vector of image URLs.
   */
  Vector prepareImageRange(int startImage, int endImage, String pattern)
  throws MalformedURLException {
      Vector result = new Vector(Math.abs(endImage - startImage) + 1);
      if (pattern == null) {
        pattern = "T%N.gif";
      }
      if (startImage > endImage) {
        for (int i = startImage; i >= endImage; i--) {
           result.addElement(new URL(imageSource, doSubst(pattern, i+"")));
        }
      } else {
        for (int i = startImage; i <= endImage; i++) {
           result.addElement(new URL(imageSource, doSubst(pattern, i+"")));
        }
      }
      return result;
  }
 Â
  /**
   * Initialize the applet.  Get parameters.
   */
  public void init() {
      tracker = new MediaTracker(this);
      appWidth = getSize().width;
      appHeight = getSize().height;
      try {
        String param = getParam("IMAGESOURCE");     Â
        imageSource = (param == null) ? getDocumentBase() : new URL(getDocumentBase(), param + "/");
     Â
        String href = getParam("HREF");
        if (href != null) {
           try {
             hrefURL = new URL(getDocumentBase(), href);
           } catch (MalformedURLException e) {
             showParseError(e);
           }
        }
        hrefTarget = getParam("TARGET");
        if (hrefTarget == null) {
           hrefTarget = "_top";
        }
        param = getParam("PAUSE");
        globalPause =
           (param != null) ? Integer.parseInt(param) : defaultPause;
        param = getParam("REPEAT");
        repeat = (param == null) ? true : (param.equalsIgnoreCase("y es") ||
                               param.equalsIgnoreCase("tr ue"));
        int startImage = 1;
        int endImage = 1;
        param = getParam("ENDIMAGE");
        if (param != null) {
           endImage = Integer.parseInt(param);
           param = getParam("STARTIMAGE");
           if (param != null) {
             startImage = Integer.parseInt(param);
           }
           param = getParam("NAMEPATTERN");
           images = prepareImageRange(startIma ge, endImage, param);
        } else {
           param = getParam("STARTIMAGE");
           if (param != null) {
             startImage = Integer.parseInt(param);
             param = getParam("NAMEPATTERN");
             images = prepareImageRange(startIma ge, endImage, param);
           } else {
             param = getParam("IMAGES");
             if (param == null) {
                 showStatus("No legal IMAGES, STARTIMAGE, or ENDIMAGE "+
                        "specified.");
                 error = true;
                 return;
             } else {
                 images = parseImages(param, getParam("NAMEPATTERN"));
             }
           }
        }
        param = getParam("BACKGROUND");
        if (param != null) {
           backgroundImageURL = new URL(imageSource, param);
        }
        param = getParam("BACKGROUNDCOLOR" );
        if (param != null) {
           backgroundColor = decodeColor(param);
        }
        param = getParam("STARTUP");
        if (param != null) {
           startUpImageURL = new URL(imageSource, param);
        }
        param = getParam("SOUNDSOURCE");
        soundSource = (param == null) ? imageSource : new URL(getDocumentBase(), param + "/");
     Â
        param = getParam("SOUNDS");
        if (param != null) {
           sounds = parseSounds(param, images);
        }
        param = getParam("PAUSES");
        if (param != null) {
           durations = parseDurations(param, images);
        }
        param = getParam("POSITIONS");
        if (param != null) {
           positions = parsePositions(param, images);
        }
        param = getParam("SOUNDTRACK");
        if (param != null) {
           soundtrackURL = new URL(soundSource, param);
        }
      } catch (MalformedURLException e) {
        showParseError(e);
      } catch (ParseException e) {
        showParseError(e);
      }
      setFrameNum(0);
      addMouseListener(this);
  }
  Color decodeColor(String s) {
      int val = 0;
      try {
        if (s.startsWith("0x")) {
           val = Integer.parseInt(s.substri ng(2), 16);
        } else if (s.startsWith("#")) {
           val = Integer.parseInt(s.substri ng(1), 16);
        } else if (s.startsWith("0") && s.length() > 1) {
           val = Integer.parseInt(s.substri ng(1), 8);
        } else {
           val = Integer.parseInt(s, 10);
        }
        return new Color(val);
      } catch (NumberFormatException e) {
        return null;
      }
  }
  void tellLoadingMsg(String file, String fileType) {
      showStatus("Animator: loading "+fileType+" "+file);
  }
  void tellLoadingMsg(URL url, String fileType) {
      tellLoadingMsg(url.toExter nalForm(), fileType);
  }
  void clearLoadingMessage() {
      showStatus("");
  }
 Â
  void loadError(String fileName, String fileType) {
      String errorMsg = "Animator: Couldn't load "+fileType+" "+
        fileName;
      showStatus(errorMsg);
      System.err.println(errorMs g);
      error = true;
      repaint();
  }
  void loadError(URL badURL, String fileType) {
      loadError(badURL.toExterna lForm(), fileType);
  }
  void loadErrors(Object errors[], String fileType) {
      for (int i = 0; i < errors.length; i++) {
        if (errors[i] instanceof Image) {
           URL url = (URL)imageNames.get((Image )errors[i] );
           if (url != null) {
             loadError(url, fileType);
           }
        }
      }
  }
  void showParseError(Exception e) {
      String errorMsg = "Animator: Parse error: "+e;
      showStatus(errorMsg);
      System.err.println(errorMs g);
      error = true;
      repaint();
  }
  void startPlaying() {
      if (soundtrack != null) {
        soundtrack.loop();
      }
  }
  void stopPlaying() {
      if (soundtrack != null) {
        soundtrack.stop();
      }
  }
  /**
   * Run the animation. This method is called by class Thread.
   * @see java.lang.Thread
   */
  public void run() {
      Thread me = Thread.currentThread();
      URL badURL;
     Â
      me.setPriority(Thread.MIN_ PRIORITY);
      if (! loaded) {
        try {
           // ... to do a bunch of loading.
           if (startUpImageURL != null) {
             tellLoadingMsg(startUpImag eURL, imageLabel);
             startUpImage = fetchImageAndWait(startUpI mageURL,
                                    STARTUP_ID);
             if (tracker.isErrorID(STARTUP _ID)) {
                 loadError(startUpImageURL, "start-up image");
             }
             repaint();
           }
       Â
           if (backgroundImageURL != null) {
             tellLoadingMsg(backgroundI mageURL, imageLabel);
             backgroundImage = fetchImageAndWait(backgrou ndImageURL ,
                                       BACKGROUND_ID);
             if (tracker.isErrorID(BACKGRO UND_ID)) {
                 loadError(backgroundImageU RL, "background image");
             }
             repaint();
           }
           // Fetch the animation frames
           if (!fetchImages(images)) {
             // get images in error, tell user about first of them
             Object errors[] = tracker.getErrorsAny();
             loadErrors(errors, imageLabel);
             return;
           }
           if (soundtrackURL != null && soundtrack == null) {
             tellLoadingMsg(soundtrackU RL, imageLabel);
             soundtrack = getAudioClip(soundtrackURL );
             if (soundtrack == null) {
                 loadError(soundtrackURL, "soundtrack");
                 return;
             }
           }
           if (sounds != null) {
             badURL = fetchSounds(sounds);
             if (badURL != null) {
                 loadError(badURL, soundLabel);
                 return;
             }
           }
           clearLoadingMessage();
           offScrImage = createImage(appWidth, appHeight);
           offScrGC = offScrImage.getGraphics();
           offScrGC.setColor(Color.li ghtGray);
           loaded = true;
           error = false;
        } catch (Exception e) {
           error = true;
           e.printStackTrace();
        }
      }
      if (userPause) {
        return;
      }
      if (repeat || frameNum < images.size()) {
        startPlaying();
      }
      try {
        if (images != null && images.size() > 1) {
           while (appWidth > 0 && appHeight > 0 && engine == me) {
             if (frameNum >= images.size()) {
                 if (!repeat) {
                   return;
                 }
                 setFrameNum(0);
             }
             repaint();
             if (sounds != null) {
                 AudioClip clip =
                   (AudioClip)sounds.get(fram eNumKey);
                 if (clip != null) {
                   clip.play();
                 }
             }
             try {
                 Integer pause = null;
                 if (durations != null) {
                   pause = (Integer)durations.get(fra meNumKey);
                 }
                 if (pause == null) {
                   Thread.sleep(globalPause);
                 } else {
                   Thread.sleep(pause.intValu e());
                 }
             } catch (InterruptedException e) {
                 // Should we do anything?
             }
             setFrameNum(frameNum+1);
           }
        }
      } finally {
        stopPlaying();
      }
  }
  /**
   * No need to clear anything; just paint.
   */
  public void update(Graphics g) {
      paint(g);
  }
  /**
   * Paint the current frame.
   */
  public void paint(Graphics g) {
      if (error || !loaded) {
        if (startUpImage != null) {
           if (tracker.checkID(STARTUP_I D)) {
             if (backgroundColor != null) {
                 g.setColor(backgroundColor );
                 g.fillRect(0, 0, appWidth, appHeight);
             }
             g.drawImage(startUpImage, 0, 0, this);
           }
        } else {
           if (backgroundImage != null) {
             if (tracker.checkID(BACKGROUN D_ID)) {
                 g.drawImage(backgroundImag e, 0, 0, this);
             }
           } else {
             g.clearRect(0, 0, appWidth, appHeight);
           }
        }
      } else {
        if ((images != null) && (images.size() > 0) &&
           tracker.checkID(ANIMATION_ ID)) {
           if (frameNum < images.size()) {
             if (backgroundImage == null) {
                 offScrGC.clearRect(0, 0, appWidth, appHeight);
             } else {
                 offScrGC.drawImage(backgro undImage, 0, 0, this);
             }
             Image image = (Image)images.elementAt(fr ameNum);
            Â
             Point pos = null;
             if (positions != null) {
                 pos = (Point)positions.get(frame NumKey);
             }
             if (pos != null) {
                 xPos = pos.x;
                 yPos = pos.y;
             }
             if (backgroundColor != null) {
                 offScrGC.setColor(backgrou ndColor);
                 offScrGC.fillRect(0, 0, appWidth, appHeight);
                 offScrGC.drawImage(image, xPos, yPos, backgroundColor,
                             this);
             } else {
                 offScrGC.drawImage(image, xPos, yPos, this);
             }
             g.drawImage(offScrImage, 0, 0, this);
           } else {
             // no more animation, but need to draw something
             dbg("No more animation; drawing last image.");
             if (backgroundImage == null) {
                 g.fillRect(0, 0, appWidth, appHeight);
             } else {
                 g.drawImage(backgroundImag e, 0, 0, this);
             }
             g.drawImage((Image)images. lastElemen t(), 0, 0, this);
           }
        }
      }
  }
  /**
   * Start the applet by forking an animation thread.
   */
  public void start() {
      if (engine == null) {
        engine = new Thread(this);
        engine.start();
      }
      showStatus(getAppletInfo() );
  }
  /**
   * Stop the insanity, um, applet.
   */
  public void stop() {
      if (engine != null && engine.isAlive()) {
        engine.stop();
      }
      engine = null;
  }
  /**
   * Pause the thread when the user clicks the mouse in the applet.
   * If the thread has stopped (as in a non-repeat performance),
   * restart it.
   */
  public void mousePressed(MouseEvent event) {
      if ((event.getModifiers() & InputEvent.SHIFT_MASK) != 0) {
        showDescription();
        return;
      } else if (hrefURL != null) {
        //Let mouseClicked handle this.
        return;
    } else if (loaded) {
        if (engine != null && engine.isAlive()) {
           if (userPause) {
             engine.resume();
             startPlaying();
          } else {
               engine.suspend();
               stopPlaying();
          }
          userPause = !userPause;
        } else {
          userPause = false;
          setFrameNum(0);
          engine = new Thread(this);
          engine.start();
        }
      }
  }
  public void mouseClicked(MouseEvent event) {
      if ((hrefURL != null) &&
          ((event.getModifiers() & InputEvent.SHIFT_MASK) == 0)) {
        getAppletContext().showDoc ument(href URL, hrefTarget);
      }
  }
  public void mouseReleased(MouseEvent event) {
  }
  public void mouseEntered(MouseEvent event) {
      showStatus(getAppletInfo() +" -- "+userInstructions);
  }
  public void mouseExited(MouseEvent event) {
    showStatus("");
  }
 Â
}
/**
 * ParseException: signals a parameter parsing problem.
 */
class ParseException extends Exception {
  ParseException(String s) {
      super(s);
  }
}
/**
 * DescriptionFrame: implements a pop-up "About" box.
 */
class DescriptionFrame extends Frame
               implements ActionListener {
  static final int rows = 27;
  static final int cols = 70;
 Â
  TextArea info;
  Button cancel;
  DescriptionFrame() {
      super("Animator v1.10");
      add("Center", info = new TextArea(rows, cols));
      info.setEditable(false);
      info.setBackground(Color.w hite);
      Panel buttons = new Panel();
      add("South", buttons);
      buttons.add(cancel = new Button("Cancel"));
      cancel.addActionListener(t his);
     Â
      pack();
  }
  public void show() {
      info.select(0,0);
      super.show();
  }
  void tell(String s) {
      info.append(s);
  }
  public void actionPerformed(ActionEven t e) {
      setVisible(false);
  }
}
I want to be able to have 4 items in a row, move one of them into the background getting smaller and moving one item at a time into the background as well, lets say from right to left. Then be able to bring them back to the foregroung one at a time in the opposite direction. If so what would have to be done. I know this is a big question. Answer the first part of the question, and if so I will break the question up into any questions.
import java.awt.*;
import java.awt.event.*;
import java.applet.Applet;
import java.applet.AudioClip;
import java.util.Vector;
import java.util.Hashtable;
import java.util.Enumeration;
import java.net.URL;
import java.net.MalformedURLExcep
/**
 * An applet that plays a sequence of images, as a loop or a one-shot.
 * Can have a soundtrack and/or sound effects tied to individual frames.
 * See the <a href="http://java.sun.com/applets/applets/Animator/">Animator
 * home page</a> for details and updates.
 *
 * @author Herb Jellinek
 * @version 1.10, 02/05/97
 */
public class Animator extends Applet
           implements Runnable,
                       MouseListener {
 Â
  /**
   * The images, in display order (Images).
   */
  Vector images = null;
  /**
   * A table that maps images to image names - for error messages.
   */
  Hashtable imageNames = new Hashtable(10);
  /**
   * Duration of each image (Integers, in milliseconds).
   */
  Hashtable durations = null;
  /**
   * Sound effects for each image (AudioClips).
   */
  Hashtable sounds = null;
  /**
   * Position of each image (Points).
   */
  Hashtable positions = null;
  /**
   * MediaTracker 'class' ID numbers.
   */
  static final int STARTUP_ID   = 0;
  static final int BACKGROUND_ID = 1;
  static final int ANIMATION_ID  = 2;
  /**
   * Start-up image URL, if any.
   */
  URL startUpImageURL = null;
  /**
   * Start-up image, if any.
   */
  Image startUpImage = null;
  /**
   * Background image URL, if any.
   */
  URL backgroundImageURL = null;
  /**
   * Background image, if any.
   */
  Image backgroundImage = null;
  /**
   * Background color, if any.
   */
  Color backgroundColor = null;
 Â
  /**
   * The soundtrack's URL.
   */
  URL soundtrackURL = null;
  /**
   * The soundtrack.
   */
  AudioClip soundtrack = null;
  /**
   * URL to link to, if any.
   */
  URL hrefURL = null;
  /**
   * Frame target for that URL, if any.
   */
  String hrefTarget = null;
  /**
   * Our width.
   */
  int appWidth = 0;
  /**
   * Our height.
   */
  int appHeight = 0;
  /**
   * The directory or URL from which the images are loaded
   */
  URL imageSource = null;
  /**
   * The directory or URL from which the sounds are loaded
   */
  URL soundSource = null;
  /**
   * The thread animating the images.
   */
  Thread engine = null;
  /**
   * The current loop slot - index into 'images.'
   */
  int frameNum;
  /**
   * frameNum as an Object - suitable for use as a Hashtable key.
   */
  Integer frameNumKey;
 Â
  /**
   * The current X position (for painting).
   */
  int xPos = 0;
 Â
  /**
   * The current Y position (for painting).
   */
  int yPos = 0;
 Â
  /**
   * The default number of milliseconds to wait between frames.
   */
  public static final int defaultPause = 3900;
 Â
  /**
   * The global delay between images, which can be overridden by
   * the PAUSE parameter.
   */
  int globalPause = defaultPause;
  /**
   * Whether or not the thread has been paused by the user.
   */
  boolean userPause = false;
  /**
   * Repeat the animation?  If false, just play it once.
   */
  boolean repeat;
  /**
   * The offscreen image, used in double buffering
   */
  Image offScrImage;
  /**
   * The offscreen graphics context, used in double buffering
   */
  Graphics offScrGC;
  /**
   * The MediaTracker we use to load our images.
   */
  MediaTracker tracker;
 Â
  /**
   * Can we paint yet?
   */
  boolean loaded = false;
  /**
   * Was there an initialization error?
   */
  boolean error = false;
  /**
   * What we call an image file in messages.
   */
  static final String imageLabel = "image";
 Â
  /**
   * What we call a sound file in messages.
   */
  static final String soundLabel = "sound";
 Â
  /**
   * Print silly debugging info?
   */
  static final boolean debug = false;
  /**
   * Where to find the source code and documentation - for informational
   * purposes.
   */
  static final String sourceLocation =
          "http://java.sun.com/applets/applets/Animator/";
  /**
   * Instructions to the user: how to produce the popup.
   */
  static final String userInstructions = "shift-click for errors, info";
  /**
   * Applet info.
   */
  public String getAppletInfo() {
      return "Animator v1.10 (02/05/97), by Herb Jellinek";
  }
  /**
   * Parameter info.
   */
  public String[][] getParameterInfo() {
      String[][] info = {
        {"imagesource",       "URL",            "a directory"},
        {"startup",       "URL",            "image displayed at start-up"},
        {"backgroundcolor", "int",      "color (24-bit RGB number) displayed as background"},
        {"background",       "URL",            "image displayed as background"},
        {"startimage",       "int",            "index of first image"},
        {"endimage",       "int",            "index of last image"},
        {"namepattern",   "URL",      "generates indexed names"},
        {"images",      "URLs",     "list of image indices"},
        {"href",            "URL",            "page to visit on mouse-click"},
        {"target",            "name",            "frame to put that page in"},
        {"pause",           "int",            "global pause, milliseconds"},
        {"pauses",           "ints",       "individual pauses, milliseconds"},
        {"repeat",           "boolean",       "repeat? true or false"},
        {"positions",      "coordinates",       "path images will follow"},
        {"soundsource",      "URL",            "audio directory"},
        {"soundtrack",      "URL",            "background music"},
        {"sounds",            "URLs",            "list of audio samples"},
      };
      return info;
  }
  /**
   * Show a crude "About" box.  Displays credits, errors (if any), and
   * parameter values and documentation.
   */
  void showDescription() {
      DescriptionFrame description = new DescriptionFrame();
       Â
      description.tell("\t\t"+ge
      description.tell("Updates,
      description.tell("Document
      description.tell("Code base: "+getCodeBase()+"\n\n");
     Â
      Object errors[] = tracker.getErrorsAny();
      if (errors != null) {
        description.tell("Applet image errors:\n");
        for (int i = 0; i < errors.length; i++) {
           if (errors[i] instanceof Image) {
             URL url = (URL)imageNames.get((Image
             if (url != null) {
                 description.tell(" "+url+" not loaded\n");
             }
           }
        }
        description.tell("\n");
      }
     Â
      if (images == null || images.size() == 0) {
        description.tell("\n** No images loaded **\n\n");
      }
      description.tell("Applet parameters:\n");
      description.tell(" width = "+getParameter("WIDTH")+"\
      description.tell(" height = "+getParameter("HEIGHT")+"
     Â
      String params[][] = getParameterInfo();
      for (int i = 0; i < params.length; i++) {
        String name = params[i][0];
        description.tell(" "+name+" = "+getParameter(name)+
                   "\t ["+params[i][2]+"]\n");
      }
     Â
      description.show();
  }
  /**
   * Print silly debugging info to the standard output.
   */
  void dbg(String s) {
      if (debug) {
        System.out.println("> "+s);
      }
  }
  /**
   * Local version of getParameter for debugging purposes.
   */
  public String getParam(String key) {
      String result = getParameter(key);
      dbg("getParameter("+key+")
      return result;
  }
  final int setFrameNum(int newFrameNum) {
      frameNumKey = new Integer(frameNum = newFrameNum);
      return frameNum;
  }
 Â
  /**
   * Parse the IMAGES parameter.  It looks like
   * 1|2|3|4|5, etc., where each number (item) names a source image.
   *
   * @return a Vector of (URL) image file names.
   */
  Vector parseImages(String attr, String pattern)
  throws MalformedURLException {
      if (pattern == null) {
        pattern = "T%N.gif";
      }
      Vector result = new Vector(10);
      for (int i = 0; i < attr.length(); ) {
        int next = attr.indexOf('|', i);
        if (next == -1) next = attr.length();
        String file = attr.substring(i, next);
        result.addElement(new URL(imageSource, doSubst(pattern, file)));
        i = next + 1;
      }
      return result;
  }
  /**
   * Fetch an image and wait for it to come in.  Used to enforce a load
   * order for background and startup images.
   * Places URL and image in imageNames table.
   */
  Image fetchImageAndWait(URL imageURL, int trackerClass)
  throws InterruptedException {
      Image image = getImage(imageURL);
      tracker.addImage(image, trackerClass);
      imageNames.put(image, imageURL);
      tracker.waitForID(trackerC
      return image;
  }
  /**
   * Fetch the images named in the argument.  Is restartable.
   *
   * @param images a Vector of URLs
   * @return true if all went well, false otherwise.
   */
  boolean fetchImages(Vector images) {
      if (images == null) {
        return true;
      }
      int size = images.size();
      for (int i = 0; i < size; i++) {
        Object o = images.elementAt(i);
        if (o instanceof URL) {
           URL url = (URL)o;
           tellLoadingMsg(url, imageLabel);
           Image im = getImage(url);
           tracker.addImage(im, ANIMATION_ID);
           images.setElementAt(im, i);
           imageNames.put(im, url);
        }
      }
      try {
        tracker.waitForID(ANIMATIO
      } catch (InterruptedException e) {}
      if (tracker.isErrorID(ANIMATI
        return false;
      }
      return true;
  }
  /**
   * Parse the SOUNDS parameter.  It looks like
   * train.au||hello.au||stop.a
   * source image.  Empty items mean that the corresponding image
   * has no associated sound.
   *
   * @return a Hashtable of SoundClips keyed to Integer frame numbers.
   */
  Hashtable parseSounds(String attr, Vector images)
  throws MalformedURLException {
      Hashtable result = new Hashtable();
      int imageNum = 0;
      int numImages = images.size();
      for (int i = 0; i < attr.length(); ) {
        if (imageNum >= numImages) break;
       Â
        int next = attr.indexOf('|', i);
        if (next == -1) next = attr.length();
       Â
        String sound = attr.substring(i, next);
        if (sound.length() != 0) {
           result.put(new Integer(imageNum),
                  new URL(soundSource, sound));
        }
        i = next + 1;
        imageNum++;
      }
      return result;
  }
  /**
   * Fetch the sounds named in the argument.
   * Is restartable.
   *
   * @return URL of the first bogus file we hit, null if OK.
   */
  URL fetchSounds(Hashtable sounds) {
      for (Enumeration e = sounds.keys() ; e.hasMoreElements() ;) {
        Integer num = (Integer)e.nextElement();
        Object o = sounds.get(num);
        if (o instanceof URL) {
           URL file = (URL)o;
           tellLoadingMsg(file, soundLabel);
           try {
             sounds.put(num, getAudioClip(file));
           } catch (Exception ex) {
             return file;
           }
        }
      }
      return null;
  }
  /**
   * Parse the PAUSES parameter.  It looks like
   * 1000|500|||750, etc., where each item corresponds to a
   * source image.  Empty items mean that the corresponding image
   * has no special duration, and should use the global one.
   *
   * @return a Hashtable of Integer pauses keyed to Integer
   * frame numbers.
   */
  Hashtable parseDurations(String attr, Vector images) {
      Hashtable result = new Hashtable();
      int imageNum = 0;
      int numImages = images.size();
      for (int i = 0; i < attr.length(); ) {
        if (imageNum >= numImages) break;
       Â
        int next = attr.indexOf('|', i);
        if (next == -1) next = attr.length();
        if (i != next) {
           int duration = Integer.parseInt(attr.subs
           result.put(new Integer(imageNum), new Integer(duration));
        } else {
           result.put(new Integer(imageNum),
                  new Integer(globalPause));
        }
        i = next + 1;
        imageNum++;
      }
      return result;
  }
  /**
   * Parse a String of form xxx@yyy and return a Point.
   */
  Point parsePoint(String s) throws ParseException {
      int atPos = s.indexOf('@');
      if (atPos == -1) throw new ParseException("Illegal position: "+s);
      return new Point(Integer.parseInt(s.s
                 Integer.parseInt(s.substri
  }
  /**
   * Parse the POSITIONS parameter.  It looks like
   * 10@30|11@31|||12@20, etc., where each item is an X@Y coordinate
   * corresponding to a source image.  Empty items mean that the
   * corresponding image has the same position as the preceding one.
   *
   * @return a Hashtable of Points keyed to Integer frame numbers.
   */
  Hashtable parsePositions(String param, Vector images)
  throws ParseException {
      Hashtable result = new Hashtable();
      int imageNum = 0;
      int numImages = images.size();
      for (int i = 0; i < param.length(); ) {
        if (imageNum >= numImages) break;
       Â
        int next = param.indexOf('|', i);
        if (next == -1) next = param.length();
        if (i != next) {
           result.put(new Integer(imageNum),
                  parsePoint(param.substring
        }
        i = next + 1;
        imageNum++;
      }
      return result;
  }
 Â
  /**
   * Get the dimensions of an image.
   * @return the image's dimensions.
   */
  Dimension getImageDimensions(Image im) {
      return new Dimension(im.getWidth(null
  }
  /**
   * Substitute an integer some number of times in a string, subject to
   * parameter strings embedded in the string.
   * Parameter strings:
   *  %N - substitute the integer as is, with no padding.
   *  %<digit>, for example %5 - substitute the integer left-padded with
   *     zeros to <digits> digits wide.
   *  %% - substitute a '%' here.
   * @param inStr the String to substitute within
   * @param theInt the int to substitute, as a String.
   */
  String doSubst(String inStr, String theInt) {
      String padStr = "0000000000";
      int length = inStr.length();
      StringBuffer result = new StringBuffer(length);
     Â
      for (int i = 0; i < length;) {
        char ch = inStr.charAt(i);
        if (ch == '%') {
           i++;
           if (i == length) {
             result.append(ch);
           } else {
             ch = inStr.charAt(i);
             if (ch == 'N' || ch == 'n') {
                 // just stick in the number, unmolested
                 result.append(theInt);
                 i++;
             } else {
                 int pad;
                 if ((pad = Character.digit(ch, 10)) != -1) {
                   // we've got a width value
                   String numStr = theInt;
                   String scr = padStr+numStr;
                   result.append(scr.substrin
                   i++;
                 } else {
                   result.append(ch);
                   i++;
                 }
             }
           }
        } else {
           result.append(ch);
           i++;
        }
      }
      return result.toString();
  }     Â
  /**
   * Stuff a range of image names into a Vector.
   *
   * @return a Vector of image URLs.
   */
  Vector prepareImageRange(int startImage, int endImage, String pattern)
  throws MalformedURLException {
      Vector result = new Vector(Math.abs(endImage - startImage) + 1);
      if (pattern == null) {
        pattern = "T%N.gif";
      }
      if (startImage > endImage) {
        for (int i = startImage; i >= endImage; i--) {
           result.addElement(new URL(imageSource, doSubst(pattern, i+"")));
        }
      } else {
        for (int i = startImage; i <= endImage; i++) {
           result.addElement(new URL(imageSource, doSubst(pattern, i+"")));
        }
      }
      return result;
  }
 Â
  /**
   * Initialize the applet.  Get parameters.
   */
  public void init() {
      tracker = new MediaTracker(this);
      appWidth = getSize().width;
      appHeight = getSize().height;
      try {
        String param = getParam("IMAGESOURCE");     Â
        imageSource = (param == null) ? getDocumentBase() : new URL(getDocumentBase(), param + "/");
     Â
        String href = getParam("HREF");
        if (href != null) {
           try {
             hrefURL = new URL(getDocumentBase(), href);
           } catch (MalformedURLException e) {
             showParseError(e);
           }
        }
        hrefTarget = getParam("TARGET");
        if (hrefTarget == null) {
           hrefTarget = "_top";
        }
        param = getParam("PAUSE");
        globalPause =
           (param != null) ? Integer.parseInt(param) : defaultPause;
        param = getParam("REPEAT");
        repeat = (param == null) ? true : (param.equalsIgnoreCase("y
                               param.equalsIgnoreCase("tr
        int startImage = 1;
        int endImage = 1;
        param = getParam("ENDIMAGE");
        if (param != null) {
           endImage = Integer.parseInt(param);
           param = getParam("STARTIMAGE");
           if (param != null) {
             startImage = Integer.parseInt(param);
           }
           param = getParam("NAMEPATTERN");
           images = prepareImageRange(startIma
        } else {
           param = getParam("STARTIMAGE");
           if (param != null) {
             startImage = Integer.parseInt(param);
             param = getParam("NAMEPATTERN");
             images = prepareImageRange(startIma
           } else {
             param = getParam("IMAGES");
             if (param == null) {
                 showStatus("No legal IMAGES, STARTIMAGE, or ENDIMAGE "+
                        "specified.");
                 error = true;
                 return;
             } else {
                 images = parseImages(param, getParam("NAMEPATTERN"));
             }
           }
        }
        param = getParam("BACKGROUND");
        if (param != null) {
           backgroundImageURL = new URL(imageSource, param);
        }
        param = getParam("BACKGROUNDCOLOR"
        if (param != null) {
           backgroundColor = decodeColor(param);
        }
        param = getParam("STARTUP");
        if (param != null) {
           startUpImageURL = new URL(imageSource, param);
        }
        param = getParam("SOUNDSOURCE");
        soundSource = (param == null) ? imageSource : new URL(getDocumentBase(), param + "/");
     Â
        param = getParam("SOUNDS");
        if (param != null) {
           sounds = parseSounds(param, images);
        }
        param = getParam("PAUSES");
        if (param != null) {
           durations = parseDurations(param, images);
        }
        param = getParam("POSITIONS");
        if (param != null) {
           positions = parsePositions(param, images);
        }
        param = getParam("SOUNDTRACK");
        if (param != null) {
           soundtrackURL = new URL(soundSource, param);
        }
      } catch (MalformedURLException e) {
        showParseError(e);
      } catch (ParseException e) {
        showParseError(e);
      }
      setFrameNum(0);
      addMouseListener(this);
  }
  Color decodeColor(String s) {
      int val = 0;
      try {
        if (s.startsWith("0x")) {
           val = Integer.parseInt(s.substri
        } else if (s.startsWith("#")) {
           val = Integer.parseInt(s.substri
        } else if (s.startsWith("0") && s.length() > 1) {
           val = Integer.parseInt(s.substri
        } else {
           val = Integer.parseInt(s, 10);
        }
        return new Color(val);
      } catch (NumberFormatException e) {
        return null;
      }
  }
  void tellLoadingMsg(String file, String fileType) {
      showStatus("Animator: loading "+fileType+" "+file);
  }
  void tellLoadingMsg(URL url, String fileType) {
      tellLoadingMsg(url.toExter
  }
  void clearLoadingMessage() {
      showStatus("");
  }
 Â
  void loadError(String fileName, String fileType) {
      String errorMsg = "Animator: Couldn't load "+fileType+" "+
        fileName;
      showStatus(errorMsg);
      System.err.println(errorMs
      error = true;
      repaint();
  }
  void loadError(URL badURL, String fileType) {
      loadError(badURL.toExterna
  }
  void loadErrors(Object errors[], String fileType) {
      for (int i = 0; i < errors.length; i++) {
        if (errors[i] instanceof Image) {
           URL url = (URL)imageNames.get((Image
           if (url != null) {
             loadError(url, fileType);
           }
        }
      }
  }
  void showParseError(Exception e) {
      String errorMsg = "Animator: Parse error: "+e;
      showStatus(errorMsg);
      System.err.println(errorMs
      error = true;
      repaint();
  }
  void startPlaying() {
      if (soundtrack != null) {
        soundtrack.loop();
      }
  }
  void stopPlaying() {
      if (soundtrack != null) {
        soundtrack.stop();
      }
  }
  /**
   * Run the animation. This method is called by class Thread.
   * @see java.lang.Thread
   */
  public void run() {
      Thread me = Thread.currentThread();
      URL badURL;
     Â
      me.setPriority(Thread.MIN_
      if (! loaded) {
        try {
           // ... to do a bunch of loading.
           if (startUpImageURL != null) {
             tellLoadingMsg(startUpImag
             startUpImage = fetchImageAndWait(startUpI
                                    STARTUP_ID);
             if (tracker.isErrorID(STARTUP
                 loadError(startUpImageURL,
             }
             repaint();
           }
       Â
           if (backgroundImageURL != null) {
             tellLoadingMsg(backgroundI
             backgroundImage = fetchImageAndWait(backgrou
                                       BACKGROUND_ID);
             if (tracker.isErrorID(BACKGRO
                 loadError(backgroundImageU
             }
             repaint();
           }
           // Fetch the animation frames
           if (!fetchImages(images)) {
             // get images in error, tell user about first of them
             Object errors[] = tracker.getErrorsAny();
             loadErrors(errors, imageLabel);
             return;
           }
           if (soundtrackURL != null && soundtrack == null) {
             tellLoadingMsg(soundtrackU
             soundtrack = getAudioClip(soundtrackURL
             if (soundtrack == null) {
                 loadError(soundtrackURL, "soundtrack");
                 return;
             }
           }
           if (sounds != null) {
             badURL = fetchSounds(sounds);
             if (badURL != null) {
                 loadError(badURL, soundLabel);
                 return;
             }
           }
           clearLoadingMessage();
           offScrImage = createImage(appWidth, appHeight);
           offScrGC = offScrImage.getGraphics();
           offScrGC.setColor(Color.li
           loaded = true;
           error = false;
        } catch (Exception e) {
           error = true;
           e.printStackTrace();
        }
      }
      if (userPause) {
        return;
      }
      if (repeat || frameNum < images.size()) {
        startPlaying();
      }
      try {
        if (images != null && images.size() > 1) {
           while (appWidth > 0 && appHeight > 0 && engine == me) {
             if (frameNum >= images.size()) {
                 if (!repeat) {
                   return;
                 }
                 setFrameNum(0);
             }
             repaint();
             if (sounds != null) {
                 AudioClip clip =
                   (AudioClip)sounds.get(fram
                 if (clip != null) {
                   clip.play();
                 }
             }
             try {
                 Integer pause = null;
                 if (durations != null) {
                   pause = (Integer)durations.get(fra
                 }
                 if (pause == null) {
                   Thread.sleep(globalPause);
                 } else {
                   Thread.sleep(pause.intValu
                 }
             } catch (InterruptedException e) {
                 // Should we do anything?
             }
             setFrameNum(frameNum+1);
           }
        }
      } finally {
        stopPlaying();
      }
  }
  /**
   * No need to clear anything; just paint.
   */
  public void update(Graphics g) {
      paint(g);
  }
  /**
   * Paint the current frame.
   */
  public void paint(Graphics g) {
      if (error || !loaded) {
        if (startUpImage != null) {
           if (tracker.checkID(STARTUP_I
             if (backgroundColor != null) {
                 g.setColor(backgroundColor
                 g.fillRect(0, 0, appWidth, appHeight);
             }
             g.drawImage(startUpImage, 0, 0, this);
           }
        } else {
           if (backgroundImage != null) {
             if (tracker.checkID(BACKGROUN
                 g.drawImage(backgroundImag
             }
           } else {
             g.clearRect(0, 0, appWidth, appHeight);
           }
        }
      } else {
        if ((images != null) && (images.size() > 0) &&
           tracker.checkID(ANIMATION_
           if (frameNum < images.size()) {
             if (backgroundImage == null) {
                 offScrGC.clearRect(0, 0, appWidth, appHeight);
             } else {
                 offScrGC.drawImage(backgro
             }
             Image image = (Image)images.elementAt(fr
            Â
             Point pos = null;
             if (positions != null) {
                 pos = (Point)positions.get(frame
             }
             if (pos != null) {
                 xPos = pos.x;
                 yPos = pos.y;
             }
             if (backgroundColor != null) {
                 offScrGC.setColor(backgrou
                 offScrGC.fillRect(0, 0, appWidth, appHeight);
                 offScrGC.drawImage(image, xPos, yPos, backgroundColor,
                             this);
             } else {
                 offScrGC.drawImage(image, xPos, yPos, this);
             }
             g.drawImage(offScrImage, 0, 0, this);
           } else {
             // no more animation, but need to draw something
             dbg("No more animation; drawing last image.");
             if (backgroundImage == null) {
                 g.fillRect(0, 0, appWidth, appHeight);
             } else {
                 g.drawImage(backgroundImag
             }
             g.drawImage((Image)images.
           }
        }
      }
  }
  /**
   * Start the applet by forking an animation thread.
   */
  public void start() {
      if (engine == null) {
        engine = new Thread(this);
        engine.start();
      }
      showStatus(getAppletInfo()
  }
  /**
   * Stop the insanity, um, applet.
   */
  public void stop() {
      if (engine != null && engine.isAlive()) {
        engine.stop();
      }
      engine = null;
  }
  /**
   * Pause the thread when the user clicks the mouse in the applet.
   * If the thread has stopped (as in a non-repeat performance),
   * restart it.
   */
  public void mousePressed(MouseEvent event) {
      if ((event.getModifiers() & InputEvent.SHIFT_MASK) != 0) {
        showDescription();
        return;
      } else if (hrefURL != null) {
        //Let mouseClicked handle this.
        return;
    } else if (loaded) {
        if (engine != null && engine.isAlive()) {
           if (userPause) {
             engine.resume();
             startPlaying();
          } else {
               engine.suspend();
               stopPlaying();
          }
          userPause = !userPause;
        } else {
          userPause = false;
          setFrameNum(0);
          engine = new Thread(this);
          engine.start();
        }
      }
  }
  public void mouseClicked(MouseEvent event) {
      if ((hrefURL != null) &&
          ((event.getModifiers() & InputEvent.SHIFT_MASK) == 0)) {
        getAppletContext().showDoc
      }
  }
  public void mouseReleased(MouseEvent event) {
  }
  public void mouseEntered(MouseEvent event) {
      showStatus(getAppletInfo()
  }
  public void mouseExited(MouseEvent event) {
    showStatus("");
  }
 Â
}
/**
 * ParseException: signals a parameter parsing problem.
 */
class ParseException extends Exception {
  ParseException(String s) {
      super(s);
  }
}
/**
 * DescriptionFrame: implements a pop-up "About" box.
 */
class DescriptionFrame extends Frame
               implements ActionListener {
  static final int rows = 27;
  static final int cols = 70;
 Â
  TextArea info;
  Button cancel;
  DescriptionFrame() {
      super("Animator v1.10");
      add("Center", info = new TextArea(rows, cols));
      info.setEditable(false);
      info.setBackground(Color.w
      Panel buttons = new Panel();
      add("South", buttons);
      buttons.add(cancel = new Button("Cancel"));
      cancel.addActionListener(t
     Â
      pack();
  }
  public void show() {
      info.select(0,0);
      super.show();
  }
  void tell(String s) {
      info.append(s);
  }
  public void actionPerformed(ActionEven
      setVisible(false);
  }
}
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.