home *** CD-ROM | disk | FTP | other *** search
/ Java 1996 August / Java - Summer 1996.iso / rockridge / tools / packages / classes / ImageMap.java < prev    next >
Encoding:
Java Source  |  1995-11-13  |  20.1 KB  |  740 lines

  1. import browser.Applet;
  2. import awt.DIBitmap;
  3. import awt.Image;
  4. import awt.Graphics;
  5. import java.util.StringTokenizer;
  6. import java.util.Vector;
  7. import java.util.Hashtable;
  8. import net.www.html.URL;
  9.  
  10. /**
  11.  * An extensible ImageMap applet class.
  12.  * The active areas on the image are controlled by ImageArea classes
  13.  * that can be dynamically extended over the web.
  14.  *
  15.  * @author     Jim Graham
  16.  * @version     1.1, 23 Mar 1995
  17.  */
  18. class ImageMap extends Applet {
  19.     /**
  20.      * The unhighlighted image being mapped.
  21.      */
  22.     Image baseImage;
  23.  
  24.     /**
  25.      * The primary highlighted image for providing user feedback.
  26.      */
  27.     Image hlImage;
  28.  
  29.     /**
  30.      * The list of image area handling objects;
  31.      */
  32.     ImageMapArea areas[];
  33.  
  34.     /**
  35.      * The primary highlight mode to be used.
  36.      */
  37.     static final int BRIGHTER = 0;
  38.     static final int DARKER = 1;
  39.  
  40.     int hlmode = BRIGHTER;
  41.  
  42.     /**
  43.      * The percentage of highlight to apply for the primary highlight mode.
  44.      */
  45.     int hlpercent = 50;
  46.  
  47.     /**
  48.      * A Hashtable of various highlighted images for ImageAreas
  49.      * that want a custom highlight.
  50.      */
  51.     Hashtable hlImages = new Hashtable();
  52.  
  53.     /**
  54.      * Get the primary highlighted version of the baseImage.
  55.      */
  56.     Image getHighlight() {
  57.     return (hlImage == null) ? makeHighlight() : hlImage;
  58.     }
  59.  
  60.     /**
  61.      * Get a version of the baseImage with a specific highlight.
  62.      * Create a new highlight image if necessary.
  63.      */
  64.     synchronized Image getHighlight(int mode, int percent) {
  65.     Image img = (Image) hlImages.get("HL"+mode+","+percent);
  66.     if (img == null) {
  67.         img = makeHighlight(mode, percent);
  68.     }
  69.     return img;
  70.     }
  71.  
  72.     /**
  73.      * Make the primary highlighted version of the baseImage.
  74.      */
  75.     Image makeHighlight() {
  76.     return makeHighlight(hlmode, hlpercent);
  77.     }
  78.  
  79.     /**
  80.      * Create a highlighted image from a highlight mode and a percentage.
  81.      */
  82.     synchronized Image makeHighlight(int mode, int percent) {
  83.     DIBitmap sourceBitmap = baseImage.getDIBitmap();
  84.  
  85.     byte newred[] = new byte[sourceBitmap.num_colors];
  86.     byte newgreen[] = new byte[sourceBitmap.num_colors];
  87.     byte newblue[] = new byte[sourceBitmap.num_colors];
  88.     for (int i = 0; i < sourceBitmap.num_colors; i++) {
  89.         if (i == sourceBitmap.trans_index) {
  90.         newred[i] = sourceBitmap.red[i];
  91.         newgreen[i] = sourceBitmap.green[i];
  92.         newblue[i] = sourceBitmap.blue[i];
  93.         } else {
  94.         int oldred = (sourceBitmap.red[i] & 0xff);
  95.         int oldgreen = (sourceBitmap.green[i] & 0xff);
  96.         int oldblue = (sourceBitmap.blue[i] & 0xff);
  97.         switch (mode) {
  98.         case DARKER:
  99.             newred[i] = (byte) (oldred * (100 - percent) / 100);
  100.             newgreen[i] = (byte) (oldgreen * (100 - percent) / 100);
  101.             newblue[i] = (byte) (oldblue * (100 - percent) / 100);
  102.             break;
  103.         case BRIGHTER:
  104.             newred[i] = (byte) (255 - ((255 - oldred)
  105.                            * (100 - percent) / 100));
  106.             newgreen[i] = (byte) (255 - ((255 - oldgreen)
  107.                          * (100 - percent) / 100));
  108.             newblue[i] = (byte) (255 - ((255 - oldblue)
  109.                         * (100 - percent) / 100));
  110.             break;
  111.         }
  112.         }
  113.     }
  114.     DIBitmap newBitmap = new DIBitmap(sourceBitmap.width,
  115.                       sourceBitmap.height,
  116.                       sourceBitmap.num_colors,
  117.                       newred, newgreen, newblue,
  118.                       sourceBitmap.raster);
  119.     newBitmap.trans_index = sourceBitmap.trans_index;
  120.     Image newImage = item.parent.createImage(newBitmap);
  121.     if (mode == hlmode && percent == hlpercent) {
  122.         hlImage = newImage;
  123.     }
  124.     hlImages.put("HL"+mode+","+percent, newImage);
  125.     return newImage;
  126.     }
  127.  
  128.     /**
  129.      * Parse a string representing the desired highlight to be applied.
  130.      */
  131.     void parseHighlight(String s) {
  132.     if (s == null) {
  133.         return;
  134.     }
  135.     if (s.startsWith("brighter")) {
  136.         hlmode = BRIGHTER;
  137.         if (s.length() > "brighter".length()) {
  138.         hlpercent = Integer.parseInt(s.substring("brighter".length()));
  139.         }
  140.     } else if (s.startsWith("darker")) {
  141.         hlmode = DARKER;
  142.         if (s.length() > "darker".length()) {
  143.         hlpercent = Integer.parseInt(s.substring("darker".length()));
  144.         }
  145.     }
  146.     }
  147.  
  148.     /**
  149.      * Initialize the applet. Get attributes.
  150.      *
  151.      * Initialize the ImageAreas.
  152.      * Each ImageArea is a subclass of the class ImageArea, and is
  153.      * specified with an attribute of the form:
  154.      *         areaN=ImageAreaClassName,arguments...
  155.      * The ImageAreaClassName is parsed off and a new instance of that
  156.      * class is created.  The initializer for that class is passed a
  157.      * reference to the applet and the remainder of the attribute
  158.      * string, from which the class should retrieve any information it
  159.      * needs about the area it controls and the actions it needs to
  160.      * take within that area.
  161.      */
  162.     public void init() {
  163.     String s;
  164.  
  165.     parseHighlight(getAttribute("highlight"));
  166.     baseImage = getImage(getAttribute("img"));
  167.     makeHighlight();
  168.     Vector areaVec = new Vector();
  169.     int num = 1;
  170.     while (true) {
  171.         ImageMapArea newArea;
  172.         s = getAttribute("area"+num);
  173.         if (s == null) {
  174.         // Try rect for backwards compatibility.
  175.         s = getAttribute("rect"+num);
  176.         if (s == null) {
  177.             break;
  178.         }
  179.         String url = getAttribute("href"+num);
  180.         if (url != null)
  181.             s += "," + url;
  182.         newArea = new HrefArea();
  183.         } else {
  184.         int classend = s.indexOf(",");
  185.         newArea = (ImageMapArea) new (s.substring(0, classend));
  186.         s = s.substring(classend+1);
  187.         }
  188.         newArea.init(this, s);
  189.         areaVec.addElement(newArea);
  190.         num++;
  191.     }
  192.     areas = new ImageMapArea[areaVec.size()];
  193.     areaVec.copyInto(areas);
  194.     resize(baseImage.width, baseImage.height);
  195.     }
  196.  
  197.     /**
  198.      * Paint the image and all active highlights.
  199.      */
  200.     public void paint(Graphics g) {
  201.     g.drawImage(baseImage, 0, 0);
  202.     for (int i = areas.length; --i >= 0; ) {
  203.         if (areas[i].active) {
  204.         areas[i].setState(g, true);
  205.         }
  206.     }
  207.     }
  208.  
  209.     /**
  210.      * Update the active highlights on the image.
  211.      */
  212.     public void update(Graphics g) {
  213.     // First unhighlight all of the deactivated areas
  214.     for (int i = areas.length; --i >= 0; ) {
  215.         if (areas[i].active && !areas[i].entered) {
  216.         areas[i].setState(g, false);
  217.         }
  218.     }
  219.     // Then highlight all of the activated areas
  220.     for (int i = areas.length; --i >= 0; ) {
  221.         if (areas[i].entered) {
  222.         areas[i].setState(g, true);
  223.         }
  224.     }
  225.     }
  226.  
  227.     /**
  228.      * Make sure that no ImageAreas are highlighted.
  229.      */
  230.     public void mouseExit() {
  231.     boolean changed = false;
  232.  
  233.     for (int i = 0; i < areas.length; i++) {
  234.         if (areas[i].active) {
  235.         areas[i].entered = false;
  236.         changed = true;
  237.         }
  238.     }
  239.     if (changed) {
  240.         repaint();
  241.     }
  242.     }
  243.  
  244.     /**
  245.      * Find the ImageAreas that the mouse is in.
  246.      */
  247.     public void mouseMove(int x, int y) {
  248.     boolean changed = false;
  249.     boolean propagate = true;
  250.  
  251.     for (int i = 0; i < areas.length; i++) {
  252.         if (areas[i].inside(x, y)) {
  253.         areas[i].entered = propagate;
  254.         if (areas[i].terminal) {
  255.             propagate = false;
  256.         }
  257.         } else {
  258.         areas[i].entered = false;
  259.         }
  260.  
  261.         if (areas[i].active != areas[i].entered) {
  262.         changed = true;
  263.         }
  264.     }
  265.  
  266.     if (changed) {
  267.         repaint();
  268.     }
  269.     }
  270.  
  271.     int pressX;
  272.     int pressY;
  273.  
  274.     /**
  275.      * Inform all active ImageAreas of a mouse press.
  276.      */
  277.     public void mouseDown(int x, int y) {
  278.     pressX = x;
  279.     pressY = y;
  280.  
  281.     for (int i = 0; i < areas.length; i++) {
  282.         if (areas[i].inside(x, y)) {
  283.         areas[i].press(x, y);
  284.         if (areas[i].terminal) {
  285.             break;
  286.         }
  287.         }
  288.     }
  289.     }
  290.  
  291.     /**
  292.      * Inform all active ImageAreas of a mouse release.
  293.      * Only those areas that were inside the original mouseDown()
  294.      * are informed of the mouseUp.
  295.      */
  296.     public void mouseUp(int x, int y) {
  297.     for (int i = 0; i < areas.length; i++) {
  298.         if (areas[i].inside(pressX, pressY)) {
  299.         areas[i].lift(x, y);
  300.         if (areas[i].terminal) {
  301.             break;
  302.         }
  303.         }
  304.     }
  305.     }
  306.  
  307.     /**
  308.      * Inform all active ImageAreas of a mouse drag.
  309.      * Only those areas that were inside the original mouseDown()
  310.      * are informed of the mouseDrag.
  311.      */
  312.     public void mouseDrag(int x, int y) {
  313.     mouseMove(x, y);
  314.     for (int i = 0; i < areas.length; i++) {
  315.         if (areas[i].inside(pressX, pressY)) {
  316.         areas[i].drag(x, y);
  317.         if (areas[i].terminal) {
  318.             break;
  319.         }
  320.         }
  321.     }
  322.     }
  323. }
  324.  
  325. /**
  326.  * The base ImageArea class.
  327.  * This class performs the basic functions that most ImageArea
  328.  * classes will need and delegates specific actions to the subclasses.
  329.  *
  330.  * @author     Jim Graham
  331.  * @version     1.1, 23 Mar 1995
  332.  */
  333. class ImageMapArea {
  334.     /** The Applet parent that contains this ImageArea. */
  335.     ImageMap parent;
  336.  
  337.     /** The X location of the area (if rectangular). */
  338.     int X;
  339.     /** The Y location of the area (if rectangular). */
  340.     int Y;
  341.     /** The width of the area (if rectangular). */
  342.     int W;
  343.     /** The height of the area (if rectangular). */
  344.     int H;
  345.  
  346.     /**
  347.      * This flag indicates whether the user was in this area during the
  348.      * last scan of mouse locations.
  349.      */
  350.     boolean entered = false;
  351.     /** This flag indicates whether the area is currently highlighted. */
  352.     boolean active = false;
  353.  
  354.     /**
  355.      * This flag indicates whether the area is terminal.  Terminal areas
  356.      * prevent any areas which are under them from being activated when
  357.      * the mouse is inside them.  Some areas may wish to change this to
  358.      * false so that they can augment other areas that they are on top of.
  359.      */
  360.     boolean terminal = true;
  361.  
  362.     /**
  363.      * Initialize this ImageArea as called from the Applet.
  364.      * If the subclass does not override this initializer, then it
  365.      * will perform the basic functions of setting the parent applet
  366.      * and parsing out 4 numbers from the argument string which specify
  367.      * a rectangular region for the ImageArea to act on.
  368.      * The remainder of the argument string is passed to the handleArg()
  369.      * method for more specific handling by the subclass.
  370.      */
  371.     public void init(ImageMap parent, String args) {
  372.     this.parent = parent;
  373.     StringTokenizer st = new StringTokenizer(args, ", ");
  374.     X = Integer.parseInt(st.nextToken());
  375.     Y = Integer.parseInt(st.nextToken());
  376.     W = Integer.parseInt(st.nextToken());
  377.     H = Integer.parseInt(st.nextToken());
  378.     if (st.hasMoreTokens()) {
  379.         // hasMoreTokens() Skips the trailing comma
  380.         handleArg(st.nextToken(""));
  381.     } else {
  382.         handleArg(null);
  383.     }
  384.     }
  385.  
  386.     /**
  387.      * This method handles the remainder of the argument string after
  388.      * the standard initializer has parsed off the 4 rectangular
  389.      * parameters.  If the subclass does not override this method,
  390.      * the remainder will be ignored.
  391.      */
  392.     public void handleArg(String s) {
  393.     }
  394.  
  395.     /**
  396.      * This method tests to see if a point is inside this ImageArea.
  397.      * The standard method assumes a rectangular area as parsed by
  398.      * the standard initializer.  If a more complex area is required
  399.      * then this method will have to be overridden by the subclass.
  400.      */
  401.     public boolean inside(int x, int y) {
  402.     return (x >= X && x < (X + W) && y >= Y && y < (Y + H));
  403.     }
  404.  
  405.     /**
  406.      * This utility method draws a rectangular subset of a highlight
  407.      * image.
  408.      */
  409.     public void drawImage(Graphics g, Image img, int x, int y, int w, int h) {
  410.     g.clipRect(x, y, w, h);
  411.     g.drawImage(img, 0, 0);
  412.     g.clearClip();
  413.     }
  414.  
  415.     /**
  416.      * This method highlights the specified area when the user enters
  417.      * it with his mouse.  The standard highlight method is to replace
  418.      * the indicated rectangular area of the image with the primary
  419.      * highlighted image.
  420.      */
  421.     public void highlight(Graphics g, boolean on) {
  422.     drawImage(g, on ? parent.hlImage : parent.baseImage, X, Y, W, H);
  423.     }
  424.  
  425.     /**
  426.      * This method changes the active state of the ImageArea, which
  427.      * indicates whether the user is currently "inside" this area.
  428.      * It turns around and calls the highlight method which is likely
  429.      * to have been overridden by subclasses seeking a custom highlight.
  430.      */
  431.     public void setState(Graphics g, boolean on) {
  432.     highlight(g, on);
  433.     active = on;
  434.     }
  435.  
  436.     /**
  437.      * The press method is called when the user presses the mouse
  438.      * button inside the ImageArea.  The location is supplied, but
  439.      * the standard implementation is to call the overloaded method
  440.      * with no arguments.
  441.      */
  442.     public void press(int x, int y) {
  443.     press();
  444.     }
  445.  
  446.     /**
  447.      * The overloaded press method is called when the user presses the
  448.      * mouse button inside the ImageArea.  This method can be overridden
  449.      * if the ImageArea does not need to know the location of the press.
  450.      */
  451.     public void press() {
  452.     }
  453.  
  454.     /**
  455.      * The lift method is called when the user releases the mouse button.
  456.      * The location is supplied, but the standard implementation is to
  457.      * call the overloaded method with no arguments.  Only those ImageAreas
  458.      * that were informed of a press will be informed of the corresponding
  459.      * release.
  460.      */
  461.     public void lift(int x, int y) {
  462.     lift();
  463.     }
  464.  
  465.     /**
  466.      * The overloaded lift method is called when the user releases the
  467.      * mouse button.  This method can be overridden if the ImageArea
  468.      * does not need to know the location of the release.
  469.      */
  470.     public void lift() {
  471.     }
  472.  
  473.     /**
  474.      * The drag method is called when the user moves the mouse while
  475.      * the button is pressed.  Only those ImageAreas that were informed
  476.      * of a press will be informed of the corresponding mouse movements.
  477.      */
  478.     public void drag(int x, int y) {
  479.     }
  480. }
  481.  
  482. /**
  483.  * The classic "Fetch a URL" ImageArea class.
  484.  * This class extends the basic ImageArea Class to fetch a URL when
  485.  * the user clicks in the area.
  486.  *
  487.  * @author     Jim Graham
  488.  * @version     1.1, 23 Mar 1995
  489.  */
  490. class HrefArea extends ImageMapArea {
  491.     /** The URL to be fetched when the user clicks on this area. */
  492.     URL anchor;
  493.  
  494.     /**
  495.      * The argument string is the URL to be fetched.
  496.      */
  497.     public void handleArg(String arg) {
  498.     anchor = new URL(parent.documentURL, arg);
  499.     }
  500.  
  501.     /**
  502.      * The status message area is updated to show the destination URL.
  503.      * The default graphics highlight feedback is used.
  504.      */
  505.     public void highlight(Graphics g, boolean on) {
  506.     super.highlight(g, on);
  507.     parent.showStatus(on ? "Go To " + anchor.toExternalForm() : null);
  508.     }
  509.  
  510.     /**
  511.      * The new URL is fetched when the user releases the mouse button
  512.      * only if they are still in the area.
  513.      */
  514.     public void lift(int x, int y) {
  515.     if (inside(x, y)) {
  516.         parent.showDocument(anchor);
  517.     }
  518.     // Note that we should not be active, so no repaint is necessary.
  519.     }
  520. }
  521.  
  522. /**
  523.  * An audio feedback ImageArea class.
  524.  * This class extends the basic ImageArea Class to play a sound each
  525.  * time the user enters the area.
  526.  *
  527.  * @author     Jim Graham
  528.  * @version     1.1, 23 Mar 1995
  529.  */
  530. class SoundArea extends ImageMapArea {
  531.     /** The URL of the sound to be played. */
  532.     String sound;
  533.  
  534.     /**
  535.      * The argument is the URL of the sound to be played.
  536.      * This method also sets this type of area to be non-terminal.
  537.      */
  538.     public void handleArg(String arg) {
  539.     sound = arg;
  540.     terminal = false;
  541.     }
  542.  
  543.     /**
  544.      * The highlight method plays the sound in addition to the usual
  545.      * graphical highlight feedback.
  546.      */
  547.     public void highlight(Graphics g, boolean on) {
  548.     super.highlight(g, on);
  549.     if (on) {
  550.         parent.play(sound);
  551.     }
  552.     }
  553. }
  554.  
  555. /**
  556.  * A click feedback ImageArea class.
  557.  * This class extends the basic ImageArea Class to show the locations
  558.  * of clicks in the image in the status message area.  This ImageArea
  559.  * utility class is useful when setting up ImageMaps.
  560.  *
  561.  * @author     Jim Graham
  562.  * @version     1.1, 23 Mar 1995
  563.  */
  564. class ClickArea extends ImageMapArea {
  565.     /** The X location of the last mouse press. */
  566.     int startx;
  567.     /** The Y location of the last mouse press. */
  568.     int starty;
  569.  
  570.     /**
  571.      * The argument is ignored, but we use this method to set this type
  572.      * of area to be non-terminal.
  573.      */
  574.     public void handleArg(String arg) {
  575.     terminal = false;
  576.     }
  577.  
  578.     /** This class overrides the highlight method to prevent highlighting. */
  579.     public void highlight(Graphics g, boolean on) {
  580.     }
  581.  
  582.     String ptstr(int x, int y) {
  583.     return "("+x+", "+y+")";
  584.     }
  585.  
  586.     /**
  587.      * When the user presses the mouse button, start showing coordinate
  588.      * feedback in the status message line.
  589.      */
  590.     public void press(int x, int y) {
  591.     parent.showStatus("Clicked at "+ptstr(x, y));
  592.     startx = x;
  593.     starty = y;
  594.     }
  595.  
  596.     /**
  597.      * Update the coordinate feedback every time the user moves the mouse
  598.      * while he has the button pressed.
  599.      */
  600.     public void drag(int x, int y) {
  601.     parent.showStatus("Rectangle from "+ptstr(startx, starty)
  602.               +" to "+ptstr(x, y)
  603.               +" is "+(x-startx)+"x"+(y-starty));
  604.     }
  605.  
  606.     /**
  607.      * Update the coordinate feedback one last time when the user releases
  608.      * the mouse button.
  609.      */
  610.     public void lift(int x, int y) {
  611.     drag(x, y);
  612.     }
  613. }
  614.  
  615. /**
  616.  * A message feedback ImageArea class.
  617.  * This class extends the basic ImageArea Class to show the a given
  618.  * message in the status message area when the user enters this area.
  619.  *
  620.  * @author     Jim Graham
  621.  * @version     1.1, 23 Mar 1995
  622.  */
  623. class NameArea extends ImageMapArea {
  624.     /** The string to be shown in the status message area. */
  625.     String name;
  626.  
  627.     /**
  628.      * The argument is the string to be displayed in the status message
  629.      * area.  This method also sets this type of area to be non-terminal.
  630.      */
  631.     public void handleArg(String arg) {
  632.     name = arg;
  633.     terminal = false;
  634.     }
  635.  
  636.     /**
  637.      * The highlight method displays the message in addition to the usual
  638.      * graphical highlight feedback.
  639.      */
  640.     public void highlight(Graphics g, boolean on) {
  641.     super.highlight(g, on);
  642.     parent.showStatus(on ? name : null);
  643.     }
  644.  
  645. }
  646.  
  647. /**
  648.  * An improved "Fetch a URL" ImageArea class.
  649.  * This class extends the basic ImageArea Class to fetch a URL when
  650.  * the user clicks in the area.  In addition, special custom highlights
  651.  * are used to make the area look and feel like a 3-D button.
  652.  *
  653.  * @author     Jim Graham
  654.  * @version     1.1, 23 Mar 1995
  655.  */
  656. class HrefButtonArea extends ImageMapArea {
  657.     /** The URL to be fetched when the user clicks on this area. */
  658.     URL anchor;
  659.  
  660.     /** The highlight image for the bright parts of the 3-D effect. */
  661.     Image brightImage;
  662.     /** The highlight image for the dark parts of the 3-D effect. */
  663.     Image darkImage;
  664.     /** The highlight image for the depressed part of the 3-D "button". */
  665.     Image pressImage;
  666.  
  667.     /** This flag indicates if the "button" is currently pressed. */
  668.     boolean pressed = false;
  669.  
  670.     /** The border size for the 3-D effect. */
  671.     int border = 5;
  672.  
  673.     /**
  674.      * The argument string is the URL to be fetched.
  675.      * This method also constructs the various highlight images needed
  676.      * to achieve the 3-D effect.
  677.      */
  678.     public void handleArg(String arg) {
  679.     anchor = new URL(parent.documentURL, arg);
  680.     brightImage = parent.getHighlight(parent.BRIGHTER, parent.hlpercent);
  681.     darkImage = parent.getHighlight(parent.DARKER, parent.hlpercent);
  682.     pressImage = parent.getHighlight(parent.DARKER, parent.hlpercent/2);
  683.     if (border * 2 > W || border * 2 > H) {
  684.         border = Math.min(W, H) / 2;
  685.     }
  686.     }
  687.  
  688.     /**
  689.      * A utility function to draw a 3-D button wrapped with an image.
  690.      */
  691.     public void drawButton(Graphics g) {
  692.     drawImage(g, pressed ? darkImage : brightImage, X, Y, W, border);
  693.     drawImage(g, pressed ? darkImage : brightImage, X, Y, border, H);
  694.     int R = X + W - 1;
  695.     int B = Y + H - 1;
  696.     if (pressed) {
  697.         drawImage(g, pressImage,
  698.               X+border, Y+border, W-border*2, H-border*2);
  699.     }
  700.     for (int i = 0; i < border; i++) {
  701.         drawImage(g, pressed ? brightImage : darkImage, X+i, B-i, W-i, 1);
  702.         drawImage(g, pressed ? brightImage : darkImage, R-i, Y+i, 1, H-i);
  703.     }
  704.     }
  705.  
  706.     /**
  707.      * The status message area is updated to show the destination URL.
  708.      * The graphical highlight is achieved using the drawButton method.
  709.      */
  710.     public void highlight(Graphics g, boolean on) {
  711.     if (on) {
  712.         drawButton(g);
  713.     } else {
  714.         super.highlight(g, false);
  715.     }
  716.     parent.showStatus(on ? "Go To " + anchor.toExternalForm() : null);
  717.     }
  718.  
  719.     /**
  720.      * Since the highlight changes when the button is pressed, we need
  721.      * to record the "pressed" state and induce a repaint.
  722.      */
  723.     public void press() {
  724.     parent.repaint();
  725.     pressed = true;
  726.     }
  727.  
  728.     /**
  729.      * The new URL is fetched when the user releases the mouse button
  730.      * only if they are still in the area.
  731.      */
  732.     public void lift(int x, int y) {
  733.     pressed = false;
  734.     if (inside(x, y)) {
  735.         parent.showDocument(anchor);
  736.     }
  737.     // Note that we should not be active, so no repaint is necessary.
  738.     }
  739. }
  740.