/* * @(#)ImageMap.java 1.7 96/04/24 * * Copyright (c) 1994-1996 Sun Microsystems, Inc. All Rights Reserved. * * Permission to use, copy, modify, and distribute this software * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and * without fee is hereby granted. * Please refer to the file http://java.sun.com/copy_trademarks.html * for further important copyright and trademark information and to * http://java.sun.com/licensing.html for further important licensing * information for the Java (tm) Technology. * * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. * * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). SUN * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR * HIGH RISK ACTIVITIES. */ import java.applet.Applet; import java.awt.Image; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.MediaTracker; import java.util.StringTokenizer; import java.util.Vector; import java.util.Hashtable; import java.net.URL; import java.awt.image.ImageProducer; import java.awt.image.ImageFilter; import java.awt.image.CropImageFilter; import java.awt.image.FilteredImageSource; import java.net.MalformedURLException; /** * An extensible ImageMap applet class. * The active areas on the image are controlled by ImageArea classes * that can be dynamically loaded over the net. * * @author Jim Graham * @version 1.7, 04/24/96 */ public class ImageMap extends Applet implements Runnable { /** * The unhighlighted image being mapped. */ Image baseImage; /** * The list of image area handling objects; */ ImageMapArea areas[]; /** * The primary highlight mode to be used. */ static final int BRIGHTER = 0; static final int DARKER = 1; int hlmode = BRIGHTER; /** * The percentage of highlight to apply for the primary highlight mode. */ int hlpercent = 50; /** * The MediaTracker for loading and constructing the various images. */ MediaTracker tracker; /** * Get a rectangular region of the baseImage highlighted according to * the primary highlight specification. */ Image getHighlight(int x, int y, int w, int h) { return getHighlight(x, y, w, h, hlmode, hlpercent); } /** * Get a rectangular region of the baseImage with a specific highlight. */ Image getHighlight(int x, int y, int w, int h, int mode, int percent) { return getHighlight(x, y, w, h, new HighlightFilter(mode == BRIGHTER, percent)); } /** * Get a rectangular region of the baseImage modified by an image filter. */ Image getHighlight(int x, int y, int w, int h, ImageFilter filter) { ImageFilter cropfilter = new CropImageFilter(x, y, w, h); ImageProducer prod = new FilteredImageSource(baseImage.getSource(), cropfilter); return makeImage(prod, filter, 0); } /** * Make a filtered image based on another image. */ Image makeImage(Image orig, ImageFilter filter) { return makeImage(orig.getSource(), filter); } /** * Make a filtered image based on another ImageProducer. */ Image makeImage(ImageProducer prod, ImageFilter filter) { return makeImage(prod, filter, (prod == baseImage.getSource()) ? 1 : 0); } /** * Make a filtered image based on another ImageProducer. * Add it to the media tracker using the indicated ID. */ Image makeImage(ImageProducer prod, ImageFilter filter, int ID) { Image filtered = createImage(new FilteredImageSource(prod, filter)); tracker.addImage(filtered, ID); return filtered; } /** * Add an image to the list of images to be tracked. */ void addImage(Image img) { tracker.addImage(img, 1); } /** * Parse a string representing the desired highlight to be applied. */ void parseHighlight(String s) { if (s == null) { return; } if (s.startsWith("brighter")) { hlmode = BRIGHTER; if (s.length() > "brighter".length()) { hlpercent = Integer.parseInt(s.substring("brighter".length())); } } else if (s.startsWith("darker")) { hlmode = DARKER; if (s.length() > "darker".length()) { hlpercent = Integer.parseInt(s.substring("darker".length())); } } } /** * Initialize the applet. Get attributes. * * Initialize the ImageAreas. * Each ImageArea is a subclass of the class ImageArea, and is * specified with an attribute of the form: * areaN=ImageAreaClassName,arguments... * The ImageAreaClassName is parsed off and a new instance of that * class is created. The initializer for that class is passed a * reference to the applet and the remainder of the attribute * string, from which the class should retrieve any information it * needs about the area it controls and the actions it needs to * take within that area. */ public void init() { String s; tracker = new MediaTracker(this); parseHighlight(getParameter("highlight")); introTune = getParameter("startsound"); baseImage = getImage(getDocumentBase(), getParameter("img")); Vector areaVec = new Vector(); int num = 1; while (true) { ImageMapArea newArea; s = getParameter("area"+num); if (s == null) { // Try rect for backwards compatibility. s = getParameter("rect"+num); if (s == null) { break; } try { newArea = new HighlightArea(); newArea.init(this, s); areaVec.addElement(newArea); String url = getParameter("href"+num); if (url != null) { s += "," + url; newArea = new LinkArea(); newArea.init(this, s); areaVec.addElement(newArea); } } catch (Exception e) { System.out.println("error processing: "+s); e.printStackTrace(); break; } } else { try { int classend = s.indexOf(","); String name = s.substring(0, classend); newArea = (ImageMapArea) Class.forName(name).newInstance(); s = s.substring(classend+1); newArea.init(this, s); areaVec.addElement(newArea); } catch (Exception e) { System.out.println("error processing: "+s); e.printStackTrace(); break; } } num++; } areas = new ImageMapArea[areaVec.size()]; areaVec.copyInto(areas); checkSize(); } Thread aniThread = null; String introTune = null; public void start() { if (introTune != null) try { play(new URL(getDocumentBase(), introTune)); } catch (MalformedURLException e) {} if (aniThread == null) { aniThread = new Thread(this); aniThread.setName("ImageMap Animator"); aniThread.start(); } } public void run() { Thread me = Thread.currentThread(); tracker.checkAll(true); for (int i = areas.length; --i >= 0; ) { areas[i].getMedia(); } me.setPriority(Thread.MIN_PRIORITY); while (aniThread == me) { boolean animating = false; for (int i = areas.length; --i >= 0; ) { animating = areas[i].animate() || animating; } try { synchronized(this) { wait(animating ? 100 : 0); } } catch (InterruptedException e) { break; } } } public synchronized void startAnimation() { notify(); } public synchronized void stop() { aniThread = null; notify(); for (int i = 0; i < areas.length; i++) { areas[i].exit(); } } /** * Check the size of this applet while the image is being loaded. */ void checkSize() { int w = baseImage.getWidth(this); int h = baseImage.getHeight(this); if (w > 0 && h > 0) { resize(w, h); synchronized(this) { fullrepaint = true; } repaint(0, 0, w, h); } } private boolean fullrepaint = false; private final static long UPDATERATE = 100; /** * Handle updates from images being loaded. */ public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { if ((infoflags & (WIDTH | HEIGHT)) != 0) { checkSize(); } if ((infoflags & (SOMEBITS | FRAMEBITS | ALLBITS)) != 0) { synchronized(this) { fullrepaint = true; } repaint(((infoflags & (FRAMEBITS | ALLBITS)) != 0) ? 0 : UPDATERATE, x, y, width, height); } return (infoflags & (ALLBITS | ERROR)) == 0; } /** * Paint the image and all active highlights. */ public void paint(Graphics g) { synchronized(this) { fullrepaint = false; } if (baseImage == null) { return; } g.drawImage(baseImage, 0, 0, this); if (areas != null) { for (int i = areas.length; --i >= 0; ) { areas[i].highlight(g); } } } /** * Update the active highlights on the image. */ public void update(Graphics g) { boolean full; synchronized(this) { full = fullrepaint; } if (full) { paint(g); return; } if (baseImage == null) { return; } g.drawImage(baseImage, 0, 0, this); if (areas == null) { return; } // First unhighlight all of the deactivated areas for (int i = areas.length; --i >= 0; ) { areas[i].highlight(g); } } /** * Make sure that no ImageAreas are highlighted. */ public boolean mouseExit(java.awt.Event evt, int x, int y) { for (int i = 0; i < areas.length; i++) { areas[i].checkExit(); } return true; } /** * Find the ImageAreas that the mouse is in. */ public boolean mouseMove(java.awt.Event evt, int x, int y) { boolean eaten = false; for (int i = 0; i < areas.length; i++) { if (!eaten && areas[i].inside(x, y)) { eaten = areas[i].checkEnter(x, y); } else { areas[i].checkExit(); } } return true; } int pressX; int pressY; /** * Inform all active ImageAreas of a mouse press. */ public boolean mouseDown(java.awt.Event evt, int x, int y) { pressX = x; pressY = y; for (int i = 0; i < areas.length; i++) { if (areas[i].inside(x, y)) { if (areas[i].press(x, y)) { break; } } } return true; } /** * Inform all active ImageAreas of a mouse release. * Only those areas that were inside the original mouseDown() * are informed of the mouseUp. */ public boolean mouseUp(java.awt.Event evt, int x, int y) { for (int i = 0; i < areas.length; i++) { if (areas[i].inside(pressX, pressY)) { if (areas[i].lift(x, y)) { break; } } } return true; } /** * Inform all active ImageAreas of a mouse drag. * Only those areas that were inside the original mouseDown() * are informed of the mouseUp. */ public boolean mouseDrag(java.awt.Event evt, int x, int y) { mouseMove(evt, x, y); for (int i = 0; i < areas.length; i++) { if (areas[i].inside(pressX, pressY)) { if (areas[i].drag(x, y)) { break; } } } return true; } /** * Scan all areas looking for the topmost status string. */ public void newStatus() { String msg = null; for (int i = 0; i < areas.length; i++) { msg = areas[i].getStatus(msg); } showStatus(msg); } }