/* * @(#)ImageMap.java 1.8 96/12/06 * * Copyright (c) 1994-1996 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.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.8, 12/06/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); } }