home *** CD-ROM | disk | FTP | other *** search
/ Symantec Visual Cafe for Java 2.5 / symantec-visual-cafe-2.5-database-dev-edition.iso / VCafe / Source.bin / ScrollingText.java < prev    next >
Encoding:
Java Source  |  1998-09-14  |  27.0 KB  |  1,054 lines

  1. package symantec.itools.multimedia;
  2.  
  3. import java.awt.*;
  4. import java.awt.event.*;
  5. import java.applet.Applet;
  6. import java.applet.AppletContext;
  7. import java.net.URL;
  8. import java.beans.PropertyVetoException;
  9. import java.beans.PropertyChangeListener;
  10. import java.beans.VetoableChangeListener;
  11. import java.util.ResourceBundle;
  12.  
  13. /**
  14.  * Scrolling text component.  Forms a text banner that scrolls specified
  15.  * text horizontally.  The component allows multiple messages and links.
  16.  *
  17.  * @version 1.0, Feb 2, 1997
  18.  * @author Symantec
  19.  */
  20.  
  21. //     02/02/97    RKM    Checked it in
  22. //     02/07/97    RKM    Fixed bug in paint where the font was noit being set up in the offscreen image (thanks Dave)
  23. //    05/31/97    RKM    Updated to support Java 1.1
  24. //                    Converted events
  25. //                    Made properties bound & constrained
  26. //                    Made choice to have a little more code in saving old & new properties when firing, but
  27. //                    think the code is cleaner
  28. //                    Changed updateCurrentMessage & nextPos to protected
  29. //  07/17/97    CAR marked fields transient as needed
  30. //  07/17/97    CAR changed class Mouse to implement java.io.Serializable interface
  31. //  07/17/97    CAR initialized messageList in default constructor
  32. //  08/21/97    LAB Made paint use the same image unless the size changes ala ButtonBase
  33. //                    (faster, and fixes a Win bug).
  34.  
  35. public class ScrollingText extends Canvas implements Runnable
  36. {
  37.     // Enums
  38.  
  39.     /**
  40.      * Constant which indicates that the banner should scroll from right to left
  41.      */
  42.  
  43.     public static final int SCROLL_LEFT = 0;
  44.  
  45.     /**
  46.      * Constant which indicates that the banner should scroll from left to right
  47.      */
  48.  
  49.     public static final int SCROLL_RIGHT = 1;
  50.  
  51.     // Property variables
  52.  
  53.     /**
  54.      * Current scroll direction. Default value SCROLL_LEFT.
  55.      */
  56.     protected int scrollDirection;
  57.  
  58.     /**
  59.      * Distance to scroll on each update.  Distance is in pixels. Default value 10.
  60.      */
  61.     protected int scrollUnit;                            //the incremental scrolling distance in pixels
  62.  
  63.     /**
  64.      * Controls the speed of the scroll. Default value 150.
  65.      */
  66.     protected int sleepTime;                            //controls the speed of the scroll
  67.  
  68.     /**
  69.      * Color for highlighted text. Default value is red.
  70.      */
  71.  
  72.     protected Color hiliteColor;
  73.  
  74.     /**
  75.      * List of messages to scroll across the banner.
  76.      */
  77.     protected String[] messageList;
  78.  
  79.  
  80.     /**
  81.      * URL links which correspond with messages.
  82.      */
  83.     protected URL[] linkToList;
  84.  
  85.     /**
  86.      * Location in browser to show linked HTML pages.
  87.      * Valid values are "_self" to show in the current frame;
  88.      * "_parent" show in the parent frame;
  89.      * "_top" show in the topmost frame;
  90.      * "_blank" show in a new unnamed top-level window;
  91.      * name show in a new top-level window named name.
  92.      */
  93.     protected String frame;
  94.  
  95.     // Internal variables
  96.  
  97.     /**
  98.      * If used in an applet, this is the applet's context.
  99.      */
  100.     transient protected AppletContext context;
  101.  
  102.     /**
  103.      * Thread which runs the scrolling animation.
  104.      */
  105.     transient protected Thread scrollThread = null;
  106.  
  107.     /**
  108.      * State of scrolling animation.
  109.      */
  110.     transient protected boolean suspended = false;
  111.  
  112.     /**
  113.      * Current horizontal text position.
  114.      */
  115.     transient protected int textX;
  116.  
  117.     /**
  118.      * Current vertical text position.
  119.      */
  120.     transient protected int textY;
  121.  
  122.     /**
  123.      * Width of current message text.
  124.      */
  125.     transient protected int textWidth;
  126.  
  127.     /**
  128.      * Height of current message text.
  129.      */
  130.     transient protected int textHeight;
  131.  
  132.  
  133.     /**
  134.      * Mouse over message text.
  135.      */
  136.     transient protected boolean isMouseOver = false;
  137.  
  138.     /**
  139.      * Last mouse cursor position.
  140.      */
  141.     transient protected int lastMouseX,lastMouseY;
  142.  
  143.     /**
  144.      * Status of mouse position.
  145.      */
  146.     transient protected boolean wasMouseOverText = false;
  147.  
  148.     /**
  149.      * Previous status message.
  150.      */
  151.     transient protected String wasStatusMessage = "";
  152.  
  153.  
  154.     /**
  155.      * Current message text.
  156.      */
  157.     protected String currMessage;
  158.  
  159.     /**
  160.      * Current URL link.
  161.      */
  162.     protected URL currLinkTo;
  163.  
  164.     /**
  165.      * Index of current message text and URL
  166.      */
  167.     protected int currIndex;
  168.  
  169.     // Constructor
  170.  
  171.     /**
  172.      * Construct default scrolling text banner.
  173.      */
  174.  
  175.     public ScrollingText()
  176.     {
  177.         super();
  178.  
  179.         //Set defaults for properties
  180.         scrollDirection = SCROLL_LEFT;
  181.         scrollUnit = 10;
  182.         sleepTime = 150;
  183.         hiliteColor = Color.red;
  184.         //messageList = new String[0];
  185.         
  186.         ResourceBundle res; 
  187.         try
  188.         {
  189.             res = ResourceBundle.getBundle("symantec.itools.resources.ResBundle");
  190.         }
  191.         catch(Throwable ex)
  192.         {
  193.             res = new symantec.itools.resources.ResBundle();
  194.         }
  195.         
  196.  
  197.         messageList = new String[5];
  198.         messageList[0] = res.getString("put");
  199.         messageList[1] = res.getString("some");
  200.         messageList[2] = res.getString("text");
  201.         messageList[3] = res.getString("in");
  202.         messageList[4] = res.getString("here");
  203.         linkToList = new URL[0];
  204.         frame = null;
  205.  
  206.         //Init current message variables
  207.         currMessage = "";
  208.         currLinkTo = null;
  209.         currIndex = -1;
  210.  
  211.         vetos = new symantec.itools.beans.VetoableChangeSupport(this);
  212.         changes = new symantec.itools.beans.PropertyChangeSupport(this);
  213.     }
  214.  
  215.     //
  216.     // Getters/Setters
  217.     //
  218.  
  219.     /**
  220.      * Set sleep time between scroll steps.  This function controls
  221.      * the speed of scrolling.  A lower number indicates a faster speed.
  222.      *
  223.      * @param speed number of milliseconds to sleep between scroll steps.  The smaller the
  224.      *              number the faster the scroll.  Minimum value of 30.
  225.      *
  226.      * @exception PropertyVetoException
  227.      * if the specified property value is unacceptable
  228.      */
  229.     public void setScrollInterval(int speed)
  230.         throws PropertyVetoException
  231.     {
  232.         //???RKM??? Would an exception be better here
  233.         if (speed < 30)
  234.             speed = 30;
  235.  
  236.         if (speed != sleepTime)
  237.         {
  238.             Integer oldScrollIntervalInt = new Integer(sleepTime);
  239.             Integer newScrollIntervalInt = new Integer(speed);
  240.  
  241.             vetos.fireVetoableChange("ScrollInterval", oldScrollIntervalInt, newScrollIntervalInt);
  242.  
  243.             sleepTime = speed;
  244.  
  245.             changes.firePropertyChange("ScrollInterval", oldScrollIntervalInt, newScrollIntervalInt);
  246.  
  247.         }
  248.     }
  249.  
  250.     /**
  251.      * Obtain the current scroll interval.  This is the number of milliseconds
  252.      * that the scroll thread will sleep between scroll steps.
  253.      *
  254.      * @return current scroll delay setting, in milliseconds
  255.      */
  256.     public int getScrollInterval()
  257.     {
  258.         return sleepTime;
  259.     }
  260.  
  261.     /**
  262.      * Specify the size of each scroll step.
  263.      *
  264.      * @param unit the number of pixels to move the text on each scroll step
  265.      *
  266.      * @exception PropertyVetoException
  267.      * if the specified property value is unacceptable
  268.      */
  269.     public void setScrollUnit(int unit)
  270.         throws PropertyVetoException
  271.     {
  272.         //???RKM??? Would an exception be better here
  273.         if (unit < 1)
  274.             unit = 1;
  275.  
  276.         if (scrollUnit != unit)
  277.         {
  278.             Integer oldScrollUnitInt = new Integer(scrollUnit);
  279.             Integer newScrollUnitInt = new Integer(unit);
  280.  
  281.             vetos.fireVetoableChange("ScrollUnit", oldScrollUnitInt, newScrollUnitInt);
  282.  
  283.             scrollUnit = unit;
  284.  
  285.             changes.firePropertyChange("ScrollUnit", oldScrollUnitInt, newScrollUnitInt);
  286.         }
  287.     }
  288.  
  289.     /**
  290.      * Obtain the current size of the scroll step.
  291.      *
  292.      * @return the number of pixels that text moves on each scroll step
  293.      *
  294.      */
  295.     public int getScrollUnit()
  296.     {
  297.         return scrollUnit;
  298.     }
  299.  
  300.     /**
  301.      * Specify the banner's current scroll direction.
  302.      *
  303.      * @param dir direction to scroll. Valid values are SCROLL_LEFT and SCROLL_RIGHT
  304.      *
  305.      * @exception PropertyVetoException
  306.      * if the specified property value is unacceptable
  307.      */
  308.     public void setScrollDirection(int dir)
  309.         throws PropertyVetoException
  310.     {
  311.         //???RKM??? Would an exception be better here
  312.         if (dir != SCROLL_LEFT && dir != SCROLL_RIGHT)
  313.             dir = SCROLL_RIGHT;
  314.  
  315.         if (scrollDirection != dir)
  316.         {
  317.             Integer oldScrollDirectionInt = new Integer(scrollDirection);
  318.             Integer newScrollDirectionInt = new Integer(dir);
  319.  
  320.             vetos.fireVetoableChange("ScrollDirection", oldScrollDirectionInt, newScrollDirectionInt);
  321.  
  322.             scrollDirection = dir;
  323.  
  324.             changes.firePropertyChange("ScrollDirection", oldScrollDirectionInt, newScrollDirectionInt);
  325.         }
  326.     }
  327.  
  328.     /**
  329.      * Obtain the current direction of scrolling.
  330.      *
  331.      * @return the current direction, either SCROLL_LEFT or SCROLL_RIGHT
  332.      */
  333.     public int getScrollDirection()
  334.     {
  335.         return scrollDirection;
  336.     }
  337.  
  338.     /**
  339.      * Set the highlight color for text.  Text is highlighted in this color when the mouse cursor is over
  340.      * it and it contains a non-null link.
  341.      *
  342.      * @param newHiliteColor color for highlighted text.
  343.      *
  344.      * @exception PropertyVetoException
  345.      * if the specified property value is unacceptable
  346.      */
  347.     public void setHiliteColor(Color newHiliteColor)
  348.         throws PropertyVetoException
  349.     {
  350.         if (!hiliteColor.equals(newHiliteColor))
  351.         {
  352.             Color oldColor = hiliteColor;
  353.             Color newColor = newHiliteColor;
  354.  
  355.             vetos.fireVetoableChange("HiliteColor", oldColor, newColor);
  356.  
  357.             hiliteColor = newHiliteColor;
  358.  
  359.             changes.firePropertyChange("HiliteColor", oldColor, newColor);
  360.         }
  361.     }
  362.  
  363.     /**
  364.      * Obtain the current color for text highlighting
  365.      *
  366.      * @return current highlight color
  367.      */
  368.  
  369.     public Color getHiliteColor()
  370.     {
  371.         return hiliteColor;
  372.     }
  373.  
  374.     /**
  375.      * Specify the current list of messages.  These messages scroll sequentially
  376.      * through the banner.
  377.      *
  378.      * @param list list of messages to display
  379.      *
  380.      * @exception PropertyVetoException
  381.      * if the specified property value is unacceptable
  382.      */
  383.     public void setMessageList(String[] list)
  384.         throws PropertyVetoException
  385.     {
  386.         if (messageList != list)
  387.         {
  388.             String[] oldMessageList = messageList;
  389.             String[] newMessageList = list;
  390.  
  391.             currIndex = 0;
  392.             vetos.fireVetoableChange("MessageList",oldMessageList,newMessageList);
  393.  
  394.             messageList = list;
  395.  
  396.             changes.firePropertyChange("MessageList",oldMessageList,newMessageList);
  397.  
  398.             updateCurrentMessage(false);
  399.         }
  400.     }
  401.  
  402.     /**
  403.      * Obtain the current list of messages.
  404.      *
  405.      * @return the current list of messages being displayed
  406.      */
  407.     public String[] getMessageList()
  408.     {
  409.         return messageList;
  410.     }
  411.  
  412.     /**
  413.      * Specify the list of URLs for message links.
  414.      * Each link corresponds with a message in the message list.
  415.      * If a message has no link, a null URL should be included for that message.
  416.      *
  417.      * @param list list of URL links
  418.      *
  419.      * @exception PropertyVetoException
  420.      * if the specified property value is unacceptable
  421.      */
  422.     public void setLinkToList(URL[] list)
  423.         throws PropertyVetoException
  424.     {
  425.         if (linkToList != list)
  426.         {
  427.             URL[] oldLinkToList = linkToList;
  428.             URL[] newLinkToList = list;
  429.  
  430.             currIndex = 0;
  431.             vetos.fireVetoableChange("LinkToList",oldLinkToList,newLinkToList);
  432.  
  433.             linkToList = list;
  434.  
  435.             changes.firePropertyChange("LinkToList",oldLinkToList,newLinkToList);
  436.  
  437.             updateCurrentMessage(false);
  438.         }
  439.     }
  440.  
  441.     /**
  442.      * Obtain the current set of URL links.
  443.      *
  444.      * @return list of URL links.  Each URL corresponds to the message of the same index in the message list
  445.      */
  446.     public URL[] getLinkToList()
  447.     {
  448.         return linkToList;
  449.     }
  450.  
  451.     /**
  452.      * Specify the display location of linked pages.  If the ScrollingText is in an applet this
  453.      * method determines where the linked pages are displayed.
  454.      *
  455.      * @param newFrame where to display the linked documents.
  456.      * Valid values are "_self" to show in the current frame;
  457.      * "_parent" show in the parent frame;
  458.      * "_top" show in the topmost frame;
  459.      * "_blank" show in a new unnamed top-level window;
  460.      * <name> show in a new top-level window named name
  461.      *
  462.      * @exception PropertyVetoException
  463.      * if the specified property value is unacceptable
  464.      */
  465.     public void setFrame(String newFrame)
  466.         throws PropertyVetoException
  467.     {
  468.         if (!symantec.itools.util.GeneralUtils.objectsEqual(frame,newFrame))
  469.         {
  470.             String oldFrame = frame;
  471.  
  472.             vetos.fireVetoableChange("Frame",oldFrame,newFrame);
  473.  
  474.             frame = newFrame;
  475.  
  476.             changes.firePropertyChange("Frame",newFrame,newFrame);
  477.         }
  478.     }
  479.  
  480.     /**
  481.      * Obtain the display location of linked pages.  If the ScrollingText is in an applet this
  482.      * method returns where the linked pages are displayed.
  483.      *
  484.      * @return Place to display the linked documents.
  485.      * Valid values are "_self" to show in the current frame;
  486.      * "_parent" show in the parent frame;
  487.      * "_top" show in the topmost frame;
  488.      * "_blank" show in a new unnamed top-level window;
  489.      * <name> show in a new top-level window named name
  490.      */
  491.     public String getFrame()
  492.     {
  493.         return frame;
  494.     }
  495.  
  496.     // Overridden methods
  497.  
  498.     /**
  499.      * Tells this component that it has been added to a container.
  500.      * This is a standard Java AWT method which gets called by the AWT when
  501.      * this component is added to a container. Typically, it is used to
  502.      * create this component's peer.
  503.      *
  504.      * It has been overridden here to start the scrolling text thread.
  505.      *
  506.      * @see #removeNotify
  507.      */
  508.     public synchronized void addNotify()
  509.     {
  510.         super.addNotify();
  511.  
  512.         //Hook up listeners
  513.         if (mouse == null)
  514.         {
  515.             mouse = new Mouse();
  516.             addMouseListener(mouse);
  517.         }
  518.         if (mouseMotion == null)
  519.         {
  520.             mouseMotion = new MouseMotion();
  521.             addMouseMotionListener(mouseMotion);
  522.         }
  523.  
  524.         //Start thread
  525.         scrollThread = new Thread(this);
  526.         scrollThread.setPriority(Thread.MIN_PRIORITY);
  527.         scrollThread.start();
  528.     }
  529.  
  530.     /**
  531.      * Tells this component that it is being removed from a container.
  532.      * This is a standard Java AWT method which gets called by the AWT when
  533.      * this component is removed from a container. Typically, it is used to
  534.      * destroy the peers of this component and all its subcomponents.
  535.      *
  536.      * It has been overridden here to stop the scrolling text thread.
  537.      *
  538.      * @see #addNotify
  539.      */
  540.     public synchronized void removeNotify()
  541.     {
  542.         //Stop thread
  543.         if (scrollThread != null)
  544.         {
  545.             scrollThread.stop();
  546.             scrollThread = null;
  547.         }
  548.  
  549.         //Unhook listeners
  550.         if (mouse != null)
  551.         {
  552.             removeMouseListener(mouse);
  553.             mouse = null;
  554.         }
  555.         if (mouseMotion != null)
  556.         {
  557.             removeMouseMotionListener(mouseMotion);
  558.             mouseMotion = null;
  559.         }
  560.  
  561.         super.removeNotify();
  562.     }
  563.  
  564.     /**
  565.      * Resumes the animation of the scrolling text.
  566.      */
  567.     public void startScrollingText() {
  568.         suspended = false;
  569.         show();
  570.     }
  571.  
  572.     /**
  573.      * Suspends the animation of the scrolling text.
  574.      */
  575.     public void stopScrollingText() {
  576.         suspended = true;
  577.     }
  578.  
  579.     /**
  580.      * Makes this component visible.
  581.      * This is a standard Java AWT method which gets called to show this
  582.      * component. If this component was invisible due to a previous hide()
  583.      * call it make this component visible again.
  584.      *
  585.      * @see #hide
  586.      */
  587.     public synchronized void show() {
  588.         super.show();
  589.         if (isVisible()) {
  590.             if (scrollThread != null) {
  591.                 scrollThread.setPriority(Thread.MAX_PRIORITY);
  592.                 scrollThread.resume();
  593.             }
  594.         }
  595.     }
  596.  
  597.     /**
  598.      * Makes this component invisible.
  599.      * This is a standard Java AWT method which gets called to hide
  600.      * this component. A hidden component cannot be seen by the user nor
  601.      * does it take up space in its container, but it does continue to
  602.      * exist.
  603.      *
  604.      * @see #show
  605.      */
  606.     public synchronized void hide() {
  607.          super.hide();
  608.          if (!isVisible()) {
  609.              if (scrollThread != null)
  610.                 scrollThread.suspend();
  611.          }
  612.     }
  613.  
  614.     /**
  615.      * ScrollingText thread body.  This method is called by the Java virtual
  616.      * machine to when this thread starts.
  617.      */
  618.     public void run()
  619.     {
  620.         createTextParams();
  621.         while (true)
  622.         {
  623.             if (!suspended)
  624.             {
  625.                 nextPos();
  626.                 try
  627.                 {
  628.                     Thread.sleep(sleepTime);
  629.                 }
  630.                 catch(Exception e)
  631.                 {
  632.                 }
  633.             }
  634.         }
  635.     }
  636.  
  637.     /**
  638.      * Move to next message in message list.  Not usually called directly.
  639.      *
  640.      * @param next if true, move to next message; otherwise reset message with current index
  641.      */
  642.     protected void updateCurrentMessage(boolean next)
  643.     {
  644.         //Increase currIndex, try to get message out
  645.  
  646.         try
  647.         {
  648.             if (next)
  649.                 currIndex++;
  650.  
  651.             if(currIndex >= messageList.length)
  652.             {
  653.                 currIndex = 0;
  654.             }
  655.             
  656.             currMessage = messageList[currIndex];
  657.         }
  658.         catch(ArrayIndexOutOfBoundsException e)
  659.         {
  660.             //Index is out of range, reset to zero
  661.             try
  662.             {
  663.                 currIndex = 0;
  664.                 currMessage = messageList[0];
  665.             }
  666.             catch(ArrayIndexOutOfBoundsException e2)
  667.             {
  668.                 //No index is valid at this point
  669.                 currMessage = "";
  670.             }
  671.         }
  672.  
  673.         if(currIndex < linkToList.length)
  674.         {
  675.             currLinkTo = linkToList[currIndex];
  676.         }
  677.         else
  678.         {
  679.             currLinkTo = null;
  680.         }
  681. /*
  682.         // Get current link to, if one is there
  683.         try
  684.         {
  685.             currLinkTo = linkToList[currIndex];
  686.         }
  687.         catch(ArrayIndexOutOfBoundsException e)
  688.         {
  689.             currLinkTo = null;
  690.         }
  691. */
  692.         createTextParams();
  693.     }
  694.  
  695.     /**
  696.      * Increment scroll step.  Moves to next message as needed. Not usually called directly.
  697.      */
  698.     protected synchronized void nextPos()
  699.     {
  700.         Dimension dim = size();
  701.         if (scrollDirection == SCROLL_LEFT)
  702.         {
  703.             textX -= scrollUnit;
  704.             if ((textX + textWidth) < 0)
  705.             {
  706.                 updateCurrentMessage(true);
  707.                 textX = dim.width;
  708.             }
  709.         }
  710.         else
  711.         {
  712.             textX += scrollUnit;
  713.             if (textX > dim.width)
  714.             {
  715.                 updateCurrentMessage(true);
  716.                 textX = -textWidth;
  717.             }
  718.         }
  719.  
  720.         repaint();
  721.     }
  722.  
  723.     /**
  724.      * Setup metrics information for current message. Sets textX, textY, textHeight and textWidth.
  725.      */
  726.     protected void createTextParams()
  727.     {
  728.         Font f = getFont();
  729.         if (f != null)
  730.         {
  731.             FontMetrics fm = getFontMetrics(f);
  732.             textHeight = fm.getHeight();
  733.  
  734.             Dimension dim = size();
  735.             textX = dim.width;
  736.             textY = ((dim.height - textHeight) >> 1) + fm.getAscent();
  737.             textWidth = fm.stringWidth(currMessage);
  738.         }
  739.     }
  740.  
  741.     /**
  742.      * Handles redrawing of this component on the screen.
  743.      * This is a standard Java AWT method which gets called by the Java
  744.      * AWT (repaint()) to handle repainting this component on the screen.
  745.      * The graphics context clipping region is set to the bounding rectangle
  746.      * of this component and its <0,0> coordinate is this component's
  747.      * top-left corner.
  748.      * Typically this method paints the background color to clear the
  749.      * component's drawing space, sets graphics context to be the foreground
  750.      * color, and then calls paint() to draw the component.
  751.      *
  752.      * It is overridden here to reduce flicker by eliminating the uneeded
  753.      * clearing of the background.
  754.      *
  755.      * @param g the graphics context
  756.      * @see java.awt.Component#repaint
  757.      * @see #paint
  758.      */
  759.     public void update(Graphics g) {
  760.         paint(g);
  761.     }
  762.  
  763.     /**
  764.      * Determines if the given point is over the current text.
  765.      *
  766.      * @param x horizontal location of point
  767.      * @param y vertical location of point
  768.      *
  769.      * @return true if given point is over the current text
  770.      */
  771.     protected boolean isMouseOverText(int x, int y)
  772.     {
  773.         if (isMouseOver)
  774.         {
  775.             if (x >= textX && x <= textX + textWidth)
  776.             {
  777.                 if (y >= textY - textHeight && y <= textY)
  778.                     return true;
  779.             }
  780.         }
  781.  
  782.         return false;
  783.     }
  784.  
  785.     /**
  786.      * Paints this component using the given graphics context.
  787.      * This is a standard Java AWT method which typically gets called
  788.      * by the AWT to handle painting this component. It paints this component
  789.      * using the given graphics context. The graphics context clipping region
  790.      * is set to the bounding rectangle of this component and its <0,0>
  791.      * coordinate is this component's top-left corner.
  792.      *
  793.      * @param g the graphics context used for painting
  794.      * @see java.awt.Component#repaint
  795.      * @see #update
  796.      */
  797.     public void paint(Graphics g)
  798.     {
  799.         Dimension dim = size();
  800.         if(isImageInvalid())
  801.         {
  802.             textImage = createImage(dim.width, dim.height);
  803.             try
  804.             {
  805.                 MediaTracker tracker = new MediaTracker(this);
  806.                 tracker.addImage(textImage, 0);
  807.                 tracker.waitForID(0);
  808.             }
  809.             catch(InterruptedException e){}
  810.         }
  811.  
  812.         Graphics textGC = textImage.getGraphics();
  813.  
  814.         //Draw the background color
  815.         textGC.setColor(getBackground());
  816.         textGC.fillRect(0, 0, dim.width, dim.height);
  817.  
  818.         textGC.setFont(getFont());
  819.  
  820.         //Determine where the mouse is
  821.         boolean mouseOverText = isMouseOverText(lastMouseX, lastMouseY);
  822.         if (mouseOverText != wasMouseOverText)
  823.         {
  824.             if (context != null)
  825.             {
  826.                 String newStatusMessage;
  827.                 if (wasMouseOverText || currLinkTo == null)
  828.                     newStatusMessage = "";
  829.                 else
  830.                     newStatusMessage = currLinkTo.toString();
  831.  
  832.                 //Update status, only if something has changed
  833.                 if (!wasStatusMessage.equals(newStatusMessage))
  834.                 {
  835.                     context.showStatus(newStatusMessage);
  836.                     wasStatusMessage = newStatusMessage;
  837.                 }
  838.             }
  839.  
  840.             wasMouseOverText = mouseOverText;
  841.         }
  842.  
  843.         //Draw the text
  844.         textGC.setColor(mouseOverText && currLinkTo != null ? hiliteColor : getForeground());
  845.         textGC.drawString(currMessage, textX, textY);
  846.         g.drawImage(textImage, 0, 0, this);
  847.         if (textGC != null)
  848.             textGC.dispose();
  849.     }
  850.  
  851.     class MouseMotion extends MouseMotionAdapter implements java.io.Serializable
  852.     {
  853.         //???RKM??? Revist JavaDoc
  854.         /**
  855.          * Processes MOUSE_MOVE events.
  856.          * This is a standard Java AWT method which gets called by the AWT
  857.          * method handleEvent() in response to receiving a MOUSE_MOVE
  858.          * event. These events occur when the mouse is moved around inside this
  859.          * component while the button is NOT pressed.
  860.          *
  861.          * @param evt the event
  862.          * @param x the component-relative horizontal coordinate of the mouse
  863.          * @param y the component-relative vertical coordinate of the mouse
  864.          *
  865.          * @return always true since the event was handled
  866.          *
  867.          * @see java.awt.Component#mouseDrag
  868.          * @see java.awt.Component#handleEvent
  869.          */
  870.         public void mouseMoved(MouseEvent event) {
  871.             isMouseOver = true;
  872.  
  873.             //Use this later to determine if the mouse is over the text
  874.             lastMouseX = event.getX();
  875.             lastMouseY = event.getY();
  876.         }
  877.     }
  878.  
  879.     class Mouse extends MouseAdapter implements java.io.Serializable
  880.     {
  881.         /**
  882.          * Processes MOUSE_EXIT events.
  883.          * This is a standard Java AWT method which gets called by the AWT
  884.          * method handleEvent() in response to receiving a MOUSE_EXIT
  885.          * event. These events occur when the mouse first leaves this
  886.          * component.
  887.          *
  888.          * @param e the event
  889.          * @param x the component-relative horizontal coordinate of the mouse
  890.          * @param y the component-relative vertical coordinate of the mouse
  891.          *
  892.          * @return always true since the event was handled
  893.          *
  894.          * @see java.awt.Component#mouseEnter
  895.          * @see java.awt.Component#handleEvent
  896.          */
  897.         public void mouseExited(MouseEvent evt) {
  898.             isMouseOver = false;
  899.         }
  900.  
  901.         /**
  902.          * Processes MOUSE_DOWN events.
  903.          * This is a standard Java AWT method which gets called by the AWT
  904.          * method handleEvent() in response to receiving a MOUSE_DOWN
  905.          * event. These events occur when the mouse button is pressed while
  906.          * inside this component.
  907.          *
  908.          * @param e the event
  909.          * @param x the component-relative horizontal coordinate of the mouse
  910.          * @param y the component-relative vertical coordinate of the mouse
  911.          *
  912.          * @return always true since the event was handled
  913.          *
  914.          * @see java.awt.Component#mouseUp
  915.          * @see java.awt.Component#handleEvent
  916.          */
  917.         public void mousePressed(MouseEvent evt) {
  918.             //If we have somewhere to go and the mouse is over the text
  919.             if (currLinkTo != null && isMouseOverText(evt.getX(), evt.getY()))
  920.             {
  921.                 if (context != null)
  922.                 {
  923.                     if (frame == null || frame.length() == 0)
  924.                         context.showDocument(currLinkTo);
  925.                     else
  926.                         context.showDocument(currLinkTo,frame);
  927.                 }
  928.             }
  929.         }
  930.     }
  931.  
  932.     /**
  933.      * Ensures that this component is laid out properly, as needed.
  934.      * This is a standard Java AWT method which gets called by the AWT to
  935.      * make sure this component and its subcomponents have a valid layout.
  936.      * If this component was made invalid with a call to invalidate(), then
  937.      * it is laid out again.
  938.      *
  939.      * It is overridden here to also find the containing applet at validation time.
  940.      *
  941.      * @see java.awt.Component#invalidate
  942.      */
  943.     public void validate()
  944.     {
  945.         // On validation, try to find the containing applet.  If we can find
  946.         // it, we don't bother doing the link...
  947.         Container c = getParent();
  948.  
  949.         while (c != null)
  950.         {
  951.             if (c instanceof Applet)
  952.             {
  953.                 setAppletContext(((Applet) c).getAppletContext());
  954.                 break;
  955.             }
  956.  
  957.             c = c.getParent();
  958.         }
  959.     }
  960.  
  961.     /**
  962.      * Specify the current applet context.
  963.      *
  964.      * @param c new applet context
  965.      */
  966.      //???RKM??? Make certain this doesn't show up as a property
  967.     protected void setAppletContext(AppletContext c)
  968.     {
  969.         context = c;
  970.     }
  971.  
  972.     /**
  973.      * Moves and/or resizes this component.
  974.      * This is a standard Java AWT method which gets called to move and/or
  975.      * resize this component. Components that are in containers with layout
  976.      * managers should not call this method, but rely on the layout manager
  977.      * instead.
  978.      *
  979.      * @param x horizontal position in the parent's coordinate space
  980.      * @param y vertical position in the parent's coordinate space
  981.      * @param width the new width
  982.      * @param height the new height
  983.      */
  984.     public synchronized void reshape(int x, int y, int width, int height) {
  985.         super.reshape(x,y,width,height);
  986.  
  987.         createTextParams();
  988.     }
  989.  
  990.     /**
  991.      * Adds a listener for all event changes.
  992.      * @param PropertyChangeListener listener the listener to add.
  993.      * @see #removePropertyChangeListener
  994.      */
  995.     public void addPropertyChangeListener(PropertyChangeListener listener)
  996.     {
  997.         //super.addPropertyChangeListener(listener);
  998.         changes.addPropertyChangeListener(listener);
  999.     }
  1000.  
  1001.     /**
  1002.      * Removes a listener for all event changes.
  1003.      * @param PropertyChangeListener listener the listener to remove.
  1004.      * @see #addPropertyChangeListener
  1005.      */
  1006.     public void removePropertyChangeListener(PropertyChangeListener listener)
  1007.     {
  1008.         //super.removePropertyChangeListener(listener);
  1009.         changes.removePropertyChangeListener(listener);
  1010.     }
  1011.  
  1012.     /**
  1013.      * Adds a vetoable listener for all event changes.
  1014.      * @param VetoableChangeListener listener the listener to add.
  1015.      * @see #removeVetoableChangeListener
  1016.      */
  1017.     public void addVetoableChangeListener(VetoableChangeListener listener)
  1018.     {
  1019.          //super.addVetoableChangeListener(listener);
  1020.         vetos.addVetoableChangeListener(listener);
  1021.     }
  1022.  
  1023.     /**
  1024.      * Removes a vetoable listener for all event changes.
  1025.      * @param VetoableChangeListener listener the listener to remove.
  1026.      * @see #addVetoableChangeListener
  1027.      */
  1028.     public void removeVetoableChangeListener(VetoableChangeListener listener)
  1029.     {
  1030.         //super.removeVetoableChangeListener(listener);
  1031.         vetos.removeVetoableChangeListener(listener);
  1032.     }
  1033.  
  1034.     /**
  1035.      * Returns true if a image has been set, but it is not the
  1036.      * size of this component.
  1037.      */
  1038.     protected boolean isImageInvalid()
  1039.     {
  1040.         Dimension s = getSize();
  1041.         return (textImage == null || s.width     != textImage.getWidth(this) || s.height != textImage.getHeight(this));
  1042.     }
  1043.  
  1044.     /**
  1045.      *    The offscreen buffer to draw in.
  1046.      */
  1047.     transient protected Image    textImage = null;
  1048.  
  1049.     private symantec.itools.beans.VetoableChangeSupport vetos;
  1050.     private symantec.itools.beans.PropertyChangeSupport changes;
  1051.     private Mouse mouse = null;
  1052.     private MouseMotion mouseMotion = null;
  1053. }
  1054.