animator.java

来自「《移动Agent技术》一书的所有章节源代码。」· Java 代码 · 共 1,135 行 · 第 1/2 页

JAVA
1,135
字号
/*
 * @(#)Animator.java	1.10 97/02/05
 *
 * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 */

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.MalformedURLException;

/**
 * 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"+getAppletInfo()+"\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(trackerClass);
	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(ANIMATION_ID);
	} catch (InterruptedException e) {}
	if (tracker.isErrorID(ANIMATION_ID)) {
	    return false;
	}

	return true;
    }

    /**
     * Parse the SOUNDS parameter.  It looks like
     * train.au||hello.au||stop.au, 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.substring(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.substring(0, atPos)),
			 Integer.parseInt(s.substring(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.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?