home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1997 October / PCO1097.ISO / FilesBBS / WIN95 / IMAP.EXE / IMap.java < prev    next >
Encoding:
Java Source  |  1997-09-02  |  23.6 KB  |  851 lines

  1. /************************************************************************************************
  2.  
  3. Image map with pop-up text displays.
  4.  
  5. <applet code="IMap.class" width=w height=h>
  6. <param name="mapimage" value="url" or "url,color">
  7. <param name="fgcolor" value="color">
  8. <param name="bgcolor" value="color">
  9. <param name="border" value="size,color">
  10. <param name="font" value="name,style,size">
  11. <param name="margins" value="n,m">
  12. <param name="outline" value="color">
  13. <param name="default" value="url" or "url,target">
  14. <param name="status" value="string">
  15. <param name="shape-1" value="rect,x1,y1,x2,y2" or "circle,x,y,r"
  16.                             or "ellipse,x,y,a,b" or "polygon,x1,y1,x2,y2,...,xn,yn">
  17. <param name="url-1" value="url" or "url,target">
  18. <param name="status-1" value="string">
  19. <param name="text-1-1" value="string" or "indent|string">
  20. <param name="text-1-2" value="string" or "indent|string">
  21. <param name="text-1-3" value="string" or "indent|string">
  22.        ...
  23. <param name="shape-n" value="rect,x1,y1,x2,y2" or "circle,x,y,r"
  24.                             or "ellipse,x,y,a,b" or "polygon,x1,y1,x2,y2,...,xn,yn">
  25. <param name="url-n" value="url" or "url,target">
  26. <param name="status-n" value="string">
  27. <param name="text-n-1" value="string" or "indent|string">
  28. <param name="text-n-2" value="string" or "indent|string">
  29. <param name="text-n-3" value="string" or "indent|string">
  30.        ...
  31. </applet>
  32.  
  33. ************************************************************************************************/
  34.  
  35. import java.awt.*;
  36. import java.awt.image.*;
  37. import java.net.*;
  38. import java.util.*;
  39. import java.applet.Applet;
  40.  
  41. /************************************************************************************************
  42.   The IMapArea class defines the area shape, position and text for the the pop-up box.
  43. ************************************************************************************************/
  44.  
  45. class IMapArea {
  46.  
  47.   // Constants:
  48.  
  49.   private final static int RECT    = 1;           // Shape types.
  50.   private final static int ELLIPSE = 2;
  51.   private final static int POLY    = 3;
  52.  
  53.   // Fields:
  54.  
  55.   private int       shape;                        // This area's shape, one of:
  56.   private Rectangle rect;                         //   rectangle
  57.   private int       x, y, a, b;                   //   ellipse
  58.   private Polygon   poly;                         //   polygon
  59.  
  60.   public URL       url    = null;                 // URL to link to when clicked.
  61.   public String    target = null;                 // Target window.
  62.   public String    status = null;                 // Status window message.
  63.  
  64.   private Vector   text   = new Vector(10, 5);    // Text line array.
  65.   private Vector   indent = new Vector(10, 5);    // Indent for each text line.
  66.   private int      count  = 0;                    // Number of text lines.
  67.  
  68.   public  int      width  = 0;                    // Width of text box.
  69.   public  int      height = 0;                    // Height of text box.
  70.  
  71.   // Constructors:
  72.  
  73.   // Create an area of the appropriate shape.
  74.  
  75.   public IMapArea(Rectangle rect) {
  76.  
  77.     this.shape = RECT;
  78.     this.rect  = rect;
  79.   }
  80.  
  81.   public IMapArea(int x, int y, int a, int b) {
  82.  
  83.     this.shape = ELLIPSE;
  84.     this.x = x;
  85.     this.y = y;
  86.     this.a = Math.abs(a);
  87.     this.b = Math.abs(b);
  88.   }
  89.  
  90.   public IMapArea(Polygon poly) {
  91.  
  92.     this.shape = POLY;
  93.     this.poly = poly;
  94.   }
  95.  
  96.   // Methods:
  97.  
  98.   // Determine the area shape.
  99.  
  100.   public boolean isRect() {
  101.  
  102.     if (this.shape == RECT)
  103.       return(true);
  104.  
  105.     return(false);
  106.   }
  107.  
  108.   public boolean isEllipse() {
  109.  
  110.     if (this.shape == ELLIPSE)
  111.       return(true);
  112.  
  113.     return(false);
  114.   }
  115.  
  116.   public boolean isPoly() {
  117.  
  118.     if (this.shape == POLY)
  119.       return(true);
  120.  
  121.     return(false);
  122.   }
  123.  
  124.   // Return the area as a shape object with the appropriate coordinates, useful for drawing.
  125.  
  126.   public Rectangle getRect() {
  127.  
  128.     if (this.isRect())
  129.       return(this.rect);
  130.  
  131.     return(new Rectangle());
  132.   }
  133.  
  134.   public Rectangle getEllipse() {
  135.  
  136.     if (this.isEllipse())
  137.       return(new Rectangle(this.x - this.a, this.y - this.b, this.a * 2, this.b * 2));
  138.  
  139.     return(new Rectangle());
  140.   }
  141.  
  142.   public Polygon getPoly() {
  143.  
  144.     if (this.isPoly())
  145.       return(this.poly);
  146.  
  147.     return(new Polygon());
  148.   }
  149.  
  150.   // Find the smallest rectangle that contains the shape.
  151.  
  152.   public Rectangle getBoundingBox() {
  153.  
  154.     Rectangle rect;
  155.  
  156.     rect = new Rectangle();
  157.     if (this.isRect())
  158.       rect = this.rect;
  159.     if (this.isEllipse())
  160.       rect = this.getEllipse();
  161.     if (this.isPoly())
  162.       rect = this.poly.getBoundingBox();
  163.     return(rect);
  164.   }
  165.  
  166.   // Set the URL to link to.
  167.  
  168.   public void setURL(URL url) {
  169.  
  170.     this.url = url;
  171.   }
  172.  
  173.   // Set the URL and target frame or window to link to.
  174.  
  175.   public void setURL(URL url, String target) {
  176.  
  177.     this.url = url;
  178.     this.target = target;
  179.   }
  180.  
  181.   // Set the message to display in the status window when this link is active.
  182.  
  183.   public void setStatusMsg(String msg) {
  184.  
  185.     this.status = msg;
  186.   }
  187.  
  188.   // Determine if a given point is inside the area.
  189.  
  190.   public boolean inside(int x, int y) {
  191.  
  192.     double v;
  193.  
  194.     if (this.isRect())
  195.       return(this.rect.inside(x, y));
  196.  
  197.     if (this.isEllipse()) {
  198.       x -= this.x;
  199.       y -= this.y;
  200.       if (Math.abs(x) > this.a || Math.abs(y) > b)
  201.         return(false);
  202.       v = (int) ((double) this.b * Math.sqrt(1.0 - (double) (x * x) / (double) (this.a * this.a)));
  203.       if (Math.abs(y) <= v) 
  204.         return(true);
  205.     }
  206.  
  207.     if (this.isPoly())
  208.       return(this.poly.inside(x, y));
  209.  
  210.     return(false);
  211.   }
  212.  
  213.   // Add a line of text with the specified indentation.
  214.  
  215.   public void addText(int n, String s) {
  216.  
  217.     this.indent.addElement(new Integer(n));
  218.     this.text.addElement(s);
  219.     this.count++;
  220.   }
  221.  
  222.   // Get a line of text.
  223.  
  224.   public String getText(int i) {
  225.  
  226.     if (i >= 0 && i < this.count)
  227.       return((String) this.text.elementAt(i));
  228.     else
  229.       return((String) null);
  230.   }
  231.  
  232.   // Get amount of indentation for a line of text.
  233.  
  234.   public int getIndent(int i) {
  235.  
  236.     if (i >= 0 && i < this.count)
  237.       return(Integer.parseInt(this.indent.elementAt(i).toString()));
  238.     else
  239.       return(0);
  240.   }
  241.  
  242.   // Get number of text lines.
  243.  
  244.   public int getCount() {
  245.  
  246.     return(this.count);
  247.   }
  248. }
  249.  
  250. /************************************************************************************************
  251.   Main applet code.
  252. ************************************************************************************************/
  253.  
  254. public class IMap extends Applet {
  255.  
  256.   // Parameters and defaults.
  257.  
  258.   Image   mapImage   = null;           // Map image.
  259.   Color   pgColor    = Color.white;    // Applet background color.
  260.   Color   fgColor    = Color.black;    // Text color.
  261.   Color   bgColor    = Color.white;    // Text box background color.
  262.   int     bdSize     = 0;              // Text box border size.
  263.   Color   bdColor;                     // Text box border color.
  264.   String  fontName   = "Dialog";       // Text font.
  265.   int     fontStyle  = Font.PLAIN;     // Text font style.
  266.   int     fontSize   = 12;             // Text font size.
  267.   int     hrznMargin = 10;             // Horizontal margin for text.
  268.   int     vertMargin = 10;             // Verticle margin for text.
  269.   boolean olFlag     = false;          // Area outline flag.
  270.   Color   olColor    = Color.black;    // Area outline color.
  271.   URL     defaultUrl = null;           // Default image map link.
  272.   String  target     = null;           // Target window or frame for the default link.
  273.   String  status     = null;           // Status window message for the default link.
  274.  
  275.   // Global variables.
  276.  
  277.   Vector areas = new Vector(10, 5);    // A list of all the defined areas.
  278.  
  279.   IMapArea area;                       // The currently active area.
  280.   int      activeArea = -1;
  281.  
  282.   Font font;                           // Font size values.
  283.   int  xAdvance;
  284.   int  yAscent;
  285.   int  yHeight;
  286.  
  287.   Dimension offDimension;              // Off-screen graphic for drawing.
  288.   Image     offImage;
  289.   Graphics  offGraphics;
  290.  
  291.   // Applet information.
  292.  
  293.   public String getAppletInfo() {
  294.  
  295.     return("IMap version 1.4\n\nCopyright 1997 by Mike Hall");
  296.   }
  297.  
  298.   public void init() {
  299.  
  300.     String s, t, u;
  301.     StringTokenizer st;
  302.     URL url;
  303.     int i, j;
  304.     Graphics g;
  305.     FontMetrics fm;
  306.     int n;
  307.  
  308.     // Take credit.
  309.  
  310.     System.out.println("IMap version 1.4, Copyright 1997 by Mike Hall.");
  311.  
  312.     // Get the map image and optional background color and start loading the image.
  313.  
  314.     try {
  315.       s = getParameter("mapimage");
  316.       if (s != null) {
  317.         st = new StringTokenizer(s, ",");
  318.         mapImage = getImage(new URL(getDocumentBase(), st.nextToken()));
  319.         prepareImage(mapImage, this);
  320.         if (st.hasMoreTokens())
  321.           pgColor = getColorParm(st.nextToken());
  322.       }
  323.     }
  324.     catch (Exception e) {}
  325.  
  326.     // Get the pop-up text box colors.
  327.  
  328.     try {
  329.       s = getParameter("fgcolor");
  330.       if (s != null)
  331.         fgColor = getColorParm(s);
  332.     }
  333.     catch (Exception e) {}
  334.     bdColor = fgColor;
  335.     try {
  336.       s = getParameter("bgcolor");
  337.       if (s != null)
  338.         bgColor = getColorParm(s);
  339.     }
  340.     catch (Exception e) {}
  341.  
  342.     // Get the pop-up text box border parameters.
  343.  
  344.     try {
  345.       s = getParameter("border");
  346.       if (s != null) {
  347.         st = new StringTokenizer(s, ",");
  348.         if ((n = Integer.parseInt(st.nextToken())) > 0)
  349.           bdSize = n;
  350.         if (st.hasMoreTokens())
  351.           bdColor = getColorParm(st.nextToken());
  352.       }
  353.     }
  354.     catch (Exception e) {}
  355.  
  356.     // Get the text box font.
  357.  
  358.     try {
  359.       s = getParameter("font");
  360.  
  361.       // Font name.
  362.  
  363.       st = new StringTokenizer(s, ",");
  364.       t = st.nextToken();
  365.       if (t.equalsIgnoreCase("Courier"))
  366.         fontName = "Courier";
  367.       else if (t.equalsIgnoreCase("Dialog"))
  368.         fontName = "Dialog";
  369.       else if (t.equalsIgnoreCase("Helvetica"))
  370.         fontName = "Helvetica";
  371.       else if (t.equalsIgnoreCase("Symbol"))
  372.         fontName = "Symbol";
  373.       else if (t.equalsIgnoreCase("TimesRoman"))
  374.         fontName = "TimesRoman";
  375.  
  376.       // Font style.
  377.  
  378.       t = st.nextToken();
  379.       if (t.equalsIgnoreCase("plain"))
  380.         fontStyle = Font.PLAIN;
  381.       else if (t.equalsIgnoreCase("bold"))
  382.         fontStyle = Font.BOLD;
  383.       else if (t.equalsIgnoreCase("italic"))
  384.         fontStyle = Font.ITALIC;
  385.       else if (t.equalsIgnoreCase("boldItalic"))
  386.         fontStyle = Font.BOLD + Font.ITALIC;
  387.  
  388.       // Font size.
  389.  
  390.       t = st.nextToken();
  391.       if ((n = Integer.parseInt(t)) > 0)
  392.           fontSize = n;
  393.     }
  394.     catch (Exception e) {}
  395.  
  396.     // Get the pop-up text box margins.
  397.  
  398.     try {
  399.       s = getParameter("margins");
  400.       if (s != null) {
  401.         st = new StringTokenizer(s, ",");
  402.         if ((n = Integer.parseInt(st.nextToken())) > 0)
  403.           hrznMargin = n;
  404.         if ((n = Integer.parseInt(st.nextToken())) > 0)
  405.           vertMargin = n;
  406.       }
  407.     }
  408.     catch (Exception e) {}
  409.  
  410.     // Get the area outline color.
  411.  
  412.     try {
  413.       s = getParameter("outline");
  414.       if (s != null) {
  415.         olColor = getColorParm(s);
  416.         olFlag = true;
  417.       }
  418.     }
  419.     catch (Exception e) {}
  420.  
  421.     // Get the default URL, target and status window message for the image map.
  422.  
  423.     try {
  424.       s = getParameter("default");
  425.       st = new StringTokenizer(s, ",");
  426.       t = st.nextToken();
  427.       try {
  428.         url = new URL(getDocumentBase(), t);
  429.         if (st.hasMoreTokens()) {
  430.           t = st.nextToken();
  431.           target = t;
  432.         }
  433.         defaultUrl = url;
  434.       }
  435.       catch (MalformedURLException e) {}
  436.     }
  437.     catch (Exception e) {}
  438.  
  439.     try {
  440.       s = getParameter("status");
  441.       if (s != null)
  442.         status = s;
  443.     }
  444.     catch (Exception e) {}
  445.  
  446.     // Get data for each image map area.
  447.  
  448.     s = null;
  449.     i = 1;
  450.     do {
  451.       try {
  452.         s = getParameter("shape-" + i);
  453.         if (s != null) {
  454.           getShape(s);
  455.  
  456.           // Get the URL, target and status window message.
  457.  
  458.           try {
  459.             t = getParameter("url-" + i);
  460.             st = new StringTokenizer(t, ",");
  461.             u = st.nextToken();
  462.             try {
  463.               url = new URL(getDocumentBase(), u);
  464.               if (st.hasMoreTokens()) {
  465.                 u = st.nextToken();
  466.                 area.setURL(url, u);
  467.               }
  468.               else
  469.                 area.setURL(url);
  470.             }
  471.             catch (MalformedURLException e) {}
  472.           }
  473.           catch (Exception e) {}
  474.  
  475.           try {
  476.             t = getParameter("status-" + i);
  477.             if (t != null)
  478.               area.setStatusMsg(t);
  479.             }
  480.           catch (Exception e) {}
  481.  
  482.           // Get text to display in the pop-up text box for this area.
  483.  
  484.           t = null;
  485.           j = 1;
  486.           do {
  487.             try {
  488.               t = getParameter("text-" + i + "-" + j);
  489.               if (t != null) {
  490.                 st = new StringTokenizer(t, "|");
  491.                 if (st.countTokens() > 1) {
  492.                   n = Integer.parseInt(st.nextToken());
  493.                   u = st.nextToken();
  494.                 }
  495.                 else {
  496.                   n = 0;
  497.                   u = t;
  498.                 }
  499.                 area.addText(n, u);
  500.               }
  501.             }
  502.             catch (Exception e) {}
  503.             j++;
  504.           } while (t != null);
  505.  
  506.           // Add the area to the list.
  507.  
  508.           areas.addElement(area);
  509.         }
  510.       }
  511.       catch (Exception e) {}
  512.       i++;
  513.     } while (s != null);
  514.  
  515.     // Trim the areas list to reclaim unused space.
  516.  
  517.     areas.trimToSize();
  518.  
  519.     // Set size values based on the font.
  520.  
  521.     g = getGraphics();
  522.     font = g.getFont();
  523.     g.setFont(font = new Font(fontName, fontStyle, fontSize));
  524.     fm = g.getFontMetrics();
  525.     xAdvance = fm.getMaxAdvance();
  526.     yAscent = fm.getMaxAscent();
  527.     yHeight = fm.getHeight();
  528.  
  529.     // Calculate the size of the pop-up text box for each area.
  530.  
  531.     for (i = 0; i < areas.size(); i++) {
  532.       area = (IMapArea) areas.elementAt(i);
  533.       for (j = 0; j < area.getCount(); j++) {
  534.         s = area.getText(j);
  535.         n = area.getIndent(j) * xAdvance + fm.stringWidth(s);
  536.         if (n > area.width)
  537.           area.width  = n;
  538.       }
  539.       area.width += 2 * hrznMargin;
  540.       area.height = area.getCount() * yHeight + 2 * vertMargin;
  541.     }
  542.   }
  543.  
  544.   private Color getColorParm(String s) {
  545.  
  546.       int r, g, b;
  547.  
  548.       // Check if a pre-defined color is specified.
  549.  
  550.       if (s.equalsIgnoreCase("black"))
  551.         return(Color.black);
  552.       if (s.equalsIgnoreCase("blue"))
  553.         return(Color.blue);
  554.       if (s.equalsIgnoreCase("cyan"))
  555.         return(Color.cyan);
  556.       if (s.equalsIgnoreCase("darkGray"))
  557.         return(Color.darkGray);
  558.       if (s.equalsIgnoreCase("gray"))
  559.         return(Color.gray);
  560.       if (s.equalsIgnoreCase("green"))
  561.         return(Color.green);
  562.       if (s.equalsIgnoreCase("lightGray"))
  563.         return(Color.lightGray);
  564.       if (s.equalsIgnoreCase("magenta"))
  565.         return(Color.magenta);
  566.       if (s.equalsIgnoreCase("orange"))
  567.         return(Color.orange);
  568.       if (s.equalsIgnoreCase("pink"))
  569.         return(Color.pink);
  570.       if (s.equalsIgnoreCase("red"))
  571.         return(Color.red);
  572.       if (s.equalsIgnoreCase("white"))
  573.         return(Color.white);
  574.       if (s.equalsIgnoreCase("yellow"))
  575.         return(Color.yellow);
  576.  
  577.       // If the color is specified in HTML format, build it from the red, green and blue values.
  578.  
  579.       if (s.length() == 7 && s.charAt(0) == '#') {
  580.         r = Integer.parseInt(s.substring(1,3),16);
  581.         g = Integer.parseInt(s.substring(3,5),16);
  582.         b = Integer.parseInt(s.substring(5,7),16);
  583.         return(new Color(r, g, b));
  584.       }
  585.  
  586.       // If we can't figure it out, default to black.
  587.  
  588.       return(Color.black);
  589.   }
  590.  
  591.   private void getShape(String s) {
  592.  
  593.     StringTokenizer st;
  594.     String t;
  595.     int x1, y1, x2, y2;
  596.     int x, y, a, b;
  597.     Polygon poly;
  598.  
  599.     // Set the area depending on the supplied shape parameters. (Note that a circle is a special
  600.     // case of an ellipse where a = b.)
  601.  
  602.     st = new StringTokenizer(s, ",");
  603.     t  = st.nextToken();
  604.     if (t.equalsIgnoreCase("rect")) {
  605.       x1 = Integer.parseInt(st.nextToken());
  606.       y1 = Integer.parseInt(st.nextToken());
  607.       x2 = Integer.parseInt(st.nextToken());
  608.       y2 = Integer.parseInt(st.nextToken());
  609.       area = new IMapArea(new Rectangle(x1, y1, x2 - x1, y2 - y1));
  610.       return;
  611.     }
  612.     if (t.equalsIgnoreCase("circle")) {
  613.       x = Integer.parseInt(st.nextToken());
  614.       y = Integer.parseInt(st.nextToken());
  615.       a = Integer.parseInt(st.nextToken());
  616.       area = new IMapArea(x, y, a, a);
  617.       return;
  618.     }
  619.     if (t.equalsIgnoreCase("ellipse")) {
  620.       x = Integer.parseInt(st.nextToken());
  621.       y = Integer.parseInt(st.nextToken());
  622.       a = Integer.parseInt(st.nextToken());
  623.       b = Integer.parseInt(st.nextToken());
  624.       area = new IMapArea(x, y, a, b);
  625.       return;
  626.     }
  627.     if (t.equalsIgnoreCase("poly")) {
  628.       poly = new Polygon();
  629.       while (st.hasMoreTokens()) {
  630.         x = Integer.parseInt(st.nextToken());
  631.         y = Integer.parseInt(st.nextToken());
  632.         poly.addPoint(x, y);
  633.       }
  634.       area = new IMapArea(poly);
  635.     }
  636.   }
  637.  
  638.   public boolean mouseExit(Event e, int x, int y) {
  639.  
  640.     // Deactivate any currently active area and clear the status window.
  641.  
  642.     activeArea = -1;
  643.     getAppletContext().showStatus("");
  644.     repaint();
  645.     return true;
  646.   }
  647.  
  648.   public boolean mouseMove(Event e, int x, int y) {
  649.  
  650.     int last;
  651.     int i;
  652.  
  653.     // Save the currently active area.
  654.  
  655.     last = activeArea;
  656.  
  657.     // If the mouse is over a area, mark it as active. (Go backwards thru the list to match the
  658.     // behavior of image maps when areas overlap.)
  659.  
  660.     activeArea = -1;
  661.     for (i = areas.size() - 1; i >= 0; i--) {
  662.       area = (IMapArea) areas.elementAt(i);
  663.       if (area.inside(x, y))
  664.         activeArea = i;
  665.     }
  666.  
  667.     // Update the display only if the active area has changed (this will save some processor
  668.     // cycles).
  669.  
  670.     if (activeArea != last) {
  671.       if (activeArea >= 0) {
  672.         area = (IMapArea) areas.elementAt(activeArea);
  673.         if (area.status != null)
  674.           getAppletContext().showStatus(area.status);
  675.         else if (area.url != null)
  676.           getAppletContext().showStatus(area.url.toString());
  677.         else
  678.           getAppletContext().showStatus("");
  679.       }
  680.       repaint();
  681.     }
  682.  
  683.     // When no area is active, show the default status window message.
  684.  
  685.     if (activeArea < 0) {
  686.       if (status != null)
  687.         getAppletContext().showStatus(status);
  688.       else if (defaultUrl != null)
  689.         getAppletContext().showStatus(defaultUrl.toString());
  690.       else
  691.         getAppletContext().showStatus("");
  692.     }
  693.  
  694.     return true;
  695.   }
  696.  
  697.   public boolean mouseDown(Event e, int x, int y) {
  698.  
  699.     // If there is currently an active area with a URL, link to it.
  700.  
  701.     if (activeArea >= 0) {
  702.       area = (IMapArea) areas.elementAt(activeArea);
  703.       if (area.url != null)
  704.         if (area.target != null)
  705.           getAppletContext().showDocument(area.url, area.target);
  706.         else
  707.           getAppletContext().showDocument(area.url);
  708.     }
  709.  
  710.     // Otherwise, if a defaut URL was specified, link to it.
  711.  
  712.     else if (defaultUrl != null) {
  713.       if (target != null)
  714.         getAppletContext().showDocument(defaultUrl, target);
  715.       else
  716.         getAppletContext().showDocument(defaultUrl);
  717.     }
  718.  
  719.     // No URL to link to, just return.
  720.  
  721.     return true;
  722.   }
  723.  
  724.   public void paint(Graphics g) {
  725.  
  726.     update(g);
  727.   }
  728.  
  729.   public void update(Graphics g) {
  730.  
  731.     Dimension d = size();
  732.     int x, y;
  733.     int i, j;
  734.     int n;
  735.     String s;
  736.     Rectangle rect;
  737.     Polygon poly;
  738.  
  739.     // Create the offscreen graphics context, if no good one exists.
  740.  
  741.     if (offGraphics == null || d.width != offDimension.width || d.height != offDimension.height) {
  742.       offDimension = d;
  743.       offImage = createImage(d.width, d.height);
  744.       offGraphics = offImage.getGraphics();
  745.     }
  746.  
  747.     // If the image map has finished loading, fill the canvas with the background color and draw
  748.     // the map image over it.
  749.  
  750.     if ((checkImage(mapImage, this) & ImageObserver.ALLBITS) == ImageObserver.ALLBITS) {
  751.       offGraphics.setColor(pgColor);
  752.       offGraphics.fillRect(0, 0, d.width, d.height);
  753.       offGraphics.drawImage(mapImage, 0, 0, this);
  754.     }
  755.  
  756.     // Otherwise, put up a message and return.
  757.  
  758.     else {
  759.       offGraphics.setColor(bgColor);
  760.       offGraphics.fillRect(0, 0, d.width, d.height);
  761.       offGraphics.setFont(font);
  762.       offGraphics.setColor(fgColor);
  763.       offGraphics.drawString("Loading image...", xAdvance, yHeight + yAscent);
  764.       g.drawImage(offImage, 0, 0, this);
  765.       return;
  766.     }
  767.  
  768.     // If there is a currently active area, process it.
  769.  
  770.     if (activeArea >= 0) {
  771.       area = (IMapArea) areas.elementAt(activeArea);
  772.       rect = area.getBoundingBox();
  773.  
  774.       // Draw the outline of the area if the outline flag is set.
  775.  
  776.       if (olFlag) {
  777.         offGraphics.setColor(olColor);
  778.         if (area.isRect()) {
  779.           rect = area.getRect();
  780.           offGraphics.drawRect(rect.x, rect.y, rect.width, rect.height);
  781.         }
  782.         else if (area.isEllipse()) {
  783.           rect = area.getEllipse();
  784.           offGraphics.drawOval(rect.x, rect.y, rect.width, rect.height);
  785.         }
  786.         else if (area.isPoly()) {
  787.           poly = area.getPoly();
  788.           offGraphics.drawPolygon(poly);
  789.           n = poly.npoints - 1;
  790.           offGraphics.drawLine(poly.xpoints[n], poly.ypoints[n], poly.xpoints[0], poly.ypoints[0]);
  791.         }
  792.       }
  793.  
  794.       // Draw the pop-up text box for the active area if there is any text.
  795.  
  796.       if (area.getCount() > 0) {
  797.  
  798.         // Determine a starting position for the text box. An attempt is made to keep it from
  799.         // overlaying the area if possible.
  800.  
  801.         x = 0; y = 0;
  802.  
  803.         // Get the x-coord for the text box.
  804.  
  805.         if ((rect.x + rect.width) / 2 < d.width / 2)
  806.           x = rect.x + rect.width + xAdvance;
  807.         else
  808.           x = Math.max(0,rect.x - area.width - xAdvance);
  809.         if (x + area.width > d.width)
  810.           x = Math.max(0, d.width - area.width - xAdvance);
  811.  
  812.         // Get the y-coord for the box.
  813.  
  814.         if (rect.intersects(new Rectangle(x,rect.y, area.width, rect.height)))
  815.           if (d.height - (rect.y + rect.height + yHeight) >= area.height)
  816.             y = rect.y + rect.height + yHeight;
  817.           else
  818.             y = rect.y - area.height - yHeight;
  819.         else if (rect.y + rect.height / 2 < d.height / 2)
  820.           y = rect.y + rect.height / 2;
  821.         else
  822.           y = Math.max(0,rect.y + rect.height / 2 - area.height);
  823.         if (y + area.height > d.height)
  824.           y = Math.max(0, d.height - area.height - yHeight);
  825.  
  826.         // Draw the pop-up box and border.
  827.  
  828.         offGraphics.setColor(bdColor);
  829.         offGraphics.fillRect(x, y, area.width, area.height);
  830.         offGraphics.setColor(bgColor);
  831.         offGraphics.fillRect(x + bdSize, y + bdSize, area.width - 2 * bdSize, area.height - 2 * bdSize);
  832.  
  833.         // Add the text.
  834.  
  835.         offGraphics.setFont(font);
  836.         offGraphics.setColor(fgColor);
  837.         for (i = 0; i < area.getCount(); i++) {
  838.           j = 0;
  839.           s = area.getText(i);
  840.           n = area.getIndent(i);
  841.           offGraphics.drawString(s, x + hrznMargin + xAdvance * n, y + vertMargin + i * yHeight + yAscent);
  842.         }
  843.       }
  844.     }
  845.  
  846.     // Paint the image onto the screen.
  847.  
  848.     g.drawImage(offImage, 0, 0, this);
  849.   }
  850. }
  851.