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 / InfoTipPanel.java < prev    next >
Encoding:
Java Source  |  1998-09-14  |  62.4 KB  |  2,081 lines

  1. package symantec.itools.awt;
  2.  
  3. import java.awt.*;
  4. import java.awt.event.ActionListener;
  5. import java.awt.event.ActionEvent;
  6. import java.awt.event.MouseAdapter;
  7. import java.awt.event.MouseEvent;
  8. import java.awt.image.ColorModel;
  9. import java.beans.PropertyVetoException;
  10. import java.beans.PropertyChangeEvent;
  11. import java.beans.PropertyChangeListener;
  12. import java.beans.VetoableChangeListener;
  13. import java.io.IOException;
  14. import java.net.URL;
  15. import symantec.itools.awt.ImagePanel;
  16. import symantec.itools.util.GeneralUtils;
  17. import symantec.itools.util.Timer;
  18. import java.util.ResourceBundle;
  19.  
  20. //  09/08/97    LAB    Conformed to Good Bean Spec.
  21. //    09/10/97    BJC Implementation of Serializable and bug fixes.
  22.  
  23. /**
  24.  * The InfoTipPanel is a panel which may contain only
  25.  * one component.
  26.  * Whenever this component receives a mouse entered event, the
  27.  * InfoTipPanel either scrolls a text message towards the button
  28.  * or pops the text up immediately next to the button.
  29.  * Whenever this component receives a mouse exit event, the
  30.  * text scrolls away from the component or disappears.
  31.  *
  32.  * @version 1.0, September 4, 1997
  33.  * @author Symantec
  34.  */
  35. public class InfoTipPanel extends ImagePanel implements Runnable, java.io.Serializable
  36. {
  37.     /**
  38.      * Defines the "from right" scroll direction.  One of four possible values for textLocation.
  39.      */
  40.     public static final int RIGHT_OF_COMPONENT = 0;
  41.     /**
  42.      * Defines the "from left" scroll direction.  One of four possible values for textLocation.
  43.      */
  44.     public static final int LEFT_OF_COMPONENT = 1;
  45.     /**
  46.      * Defines the "from top" scroll direction.  One of four possible values for textLocation.
  47.      */
  48.     public static final int ABOVE_COMPONENT = 2;
  49.     /**
  50.      * Defines the "from bottom" scroll direction.  One of four possible values for textLocation.
  51.      */
  52.     public static final int BELOW_COMPONENT = 3;
  53.  
  54.     /**
  55.      * Constructs a default InfoTipPanel.
  56.      * The panel is initialized with a null component, RIGHT_OF_COMPONENT as the scrolldirection,
  57.      * and the Auto-Position property set to true.
  58.      */
  59.     public InfoTipPanel()
  60.     {
  61.         this(null, "", RIGHT_OF_COMPONENT, true, 0, false);
  62.             //{{INIT_CONTROLS
  63.         java.util.ResourceBundle resourceBundle = java.util.ResourceBundle.getBundle("symantec.itools.awt.InfoTipPanelBundle");
  64.         setLayout(null);
  65.         setSize(20,40);
  66.         //}}
  67. }
  68.  
  69.     /**
  70.      * Constructs a new InfoTipPanel initialized with the
  71.      * specified component, text, minimum height and
  72.      * minimum width.
  73.      * @param component the component which will be added to the InfoTipPanel and
  74.      * whose mouse entered and mouse exited events will control the scrolling of the text.
  75.      * @param text the text which will be scrolled.
  76.      * @param textLocation the direction from which the text is scrolled.
  77.      * resize itself and position the component placed inside of it.
  78.      */
  79.     public InfoTipPanel(Component component, String text, int textLocation, boolean textScrollsIn, int textDelay, boolean highlightText)
  80.     {
  81.         scrollState = DONE_SCROLLING_OUT;  //the text starts fully extended, waiting to start scrolling in on a mouse entered event
  82.  
  83.         myMouseListener = new Mouse();
  84.         delayTimer = new Timer();
  85.         if (delayTimer != null)
  86.             delayTimer.addActionListener(new Action());
  87.  
  88.  
  89.         super.setLayout(null);
  90.  
  91.         try
  92.         {
  93.             setHighlightText(highlightText);
  94.             setTextHighlightColor(new Color(16776960));    //default to yellow
  95.             setTextColor(new Color(0));    //default to black
  96.             setTextScrollsIn(textScrollsIn);
  97.             setTextDelay(textDelay);
  98.             setText(text);
  99.             setTextLocation(textLocation);
  100.         }
  101.         catch(PropertyVetoException e) {}
  102.  
  103.         setComponent(component);
  104.     }
  105.  
  106.     /**
  107.      * Set the amount of time (in milliseconds) to wait after a mouse
  108.      * entered event before the text is scrolled in or popped up.
  109.      *
  110.      * @param newDelay the amount of time (in milliseconds) to wait after a mouse
  111.      * entered event before the text is scrolled in or popped up.
  112.      * @see #getTextDelay
  113.      * @exception PropertyVetoException
  114.      * if the specified property value is unacceptable
  115.      */
  116.     public void setTextDelay(int newDelay) throws PropertyVetoException
  117.     {
  118.         if (textDelay != newDelay)
  119.         {
  120.             Integer oldTextDelay = new Integer(textDelay);
  121.             Integer newTextDelay = new Integer(newDelay);
  122.  
  123.             vetos.fireVetoableChange("textDelay", oldTextDelay, newTextDelay);
  124.  
  125.             textDelay = newDelay;
  126.             delayTimer.setDelay(textDelay); //let the timer know that we will want a new delay time
  127.  
  128.             changes.firePropertyChange("textDelay", oldTextDelay, newTextDelay);
  129.         }
  130.     }
  131.  
  132.     /**
  133.      * Get the amount of time (in milliseconds) to wait after a mouse
  134.      * entered event before the text is scrolled in or popped up.
  135.      *
  136.      * @return the amount of time (in milliseconds) that this InfoTipPanel
  137.      * will wait after a mouse entered event before the text is scrolled in
  138.      * or popped up.
  139.      * @see #setTextDelay
  140.      */
  141.     public int getTextDelay()
  142.     {
  143.         return textDelay;
  144.     }
  145.  
  146.  
  147.     /**
  148.      * Sets the "Text Scrolls In" property which tells the InfoTipPanel whether
  149.      * the text should be scrolled in on a mouse over rather than just appearing
  150.      * next to the component (without scrolling in).
  151.      *
  152.      * @param the new value for this property
  153.      * @see #getTextScrollsIn
  154.      * @exception PropertyVetoException
  155.      * if the specified property value is unacceptable
  156.      */
  157.     public void setTextScrollsIn(boolean newScroll) throws PropertyVetoException
  158.     {
  159.         if (textScrollsIn != newScroll)
  160.         {
  161.             Boolean oldTextScrollsIn = new Boolean(textScrollsIn);
  162.             Boolean newTextScrollsIn = new Boolean(newScroll);
  163.  
  164.             vetos.fireVetoableChange("textScrollsIn", oldTextScrollsIn, newTextScrollsIn);
  165.  
  166.             stopScrolling();
  167.             textScrollsIn = newScroll;
  168.  
  169.             changes.firePropertyChange("textScrollsIn", oldTextScrollsIn, newTextScrollsIn);
  170.         }
  171.     }
  172.  
  173.  
  174.     /**
  175.      * Gets the value of the "Text Scrolls In" property which tells the
  176.      * InfoTipPanel whether the text should be scrolled in on a mouse over
  177.      * rather than just appearing next to the component (without scrolling in).
  178.      *
  179.      * @return the current value of this property
  180.      * @see #setTextScrollsIn
  181.      */
  182.     public boolean getTextScrollsIn()
  183.     {
  184.         return textScrollsIn;
  185.     }
  186.  
  187.  
  188.     /**
  189.      * Sets where the text will be located in this InfoTipPanel. This controls
  190.      * where the text will appear on a mouse entered event.
  191.      * Causes the component to redraw.
  192.      *
  193.      * @param newLocation the new text location: RIGHT_OF_COMPONENT,
  194.      * LEFT_OF_COMPONENT, ABOVE_COMPONENT, or BELOW_COMPONENT
  195.      * @see #getTextLocation
  196.      * @exception PropertyVetoException
  197.      * if the specified property value is unacceptable
  198.      */
  199.     public void setTextLocation(int newLocation) throws PropertyVetoException
  200.     {
  201.         if (textLocation != newLocation)
  202.         {
  203.             Integer oldTextLocation = new Integer(textLocation);
  204.             Integer newTextLocation = new Integer(newLocation);
  205.  
  206.             vetos.fireVetoableChange("textLocation", oldTextLocation, newTextLocation);
  207.  
  208.             stopScrolling();
  209.             textLocation = newLocation;
  210.  
  211.             createOffScreenBuffers();
  212.  
  213.             repaint();
  214.  
  215.             changes.firePropertyChange("textLocation", oldTextLocation, newTextLocation);
  216.         }
  217.     }
  218.  
  219.  
  220.     /**
  221.      * Gets the current value of the "Text Location" property for this InfoTipPanel.
  222.      * The text location controls where the text will appear on a mouse entered event.
  223.      *
  224.      * @return the text location: RIGHT_OF_COMPONENT, LEFT_OF_COMPONENT,
  225.      * ABOVE_COMPONENT, or BELOW_COMPONENT
  226.      * @see #setTextLocation
  227.      */
  228.     public int getTextLocation()
  229.     {
  230.         return textLocation;
  231.     }
  232.  
  233.     /**
  234.      * Sets the text in this InfoTipPanel. This is the text
  235.      * which scrolls out on a mouse entered event.
  236.      * Causes the component to redraw.
  237.      *
  238.      * @param newText the text
  239.      * @exception PropertyVetoException
  240.      * if the specified property value is unacceptable
  241.      * @see #getText
  242.      */
  243.     public void setText(String newText) throws PropertyVetoException
  244.     {
  245.         if (!symantec.itools.util.GeneralUtils.objectsEqual(text, newText))
  246.         {
  247.             String oldText = text;
  248.  
  249.             vetos.fireVetoableChange("text", oldText, newText);
  250.  
  251.             stopScrolling();
  252.  
  253.             text = newText;
  254.  
  255.             calculateTextAttributes();
  256.  
  257.             changes.firePropertyChange("text", oldText, newText);
  258.         }
  259.     }
  260.  
  261.  
  262.     /**
  263.      * Gets the current text in this InfoTipPanel. This is the text
  264.      * which scrolls out on a mouse entered event.
  265.      *
  266.      * @return the text
  267.      * @see #setText
  268.      */
  269.     public String getText()
  270.     {
  271.         return new String(text);
  272.     }
  273.  
  274.  
  275.     /**
  276.      * Sets the text color.
  277.      * Causes the InfoTipPanel to redraw.
  278.      * @param color the new color for the text
  279.      * @see #getTextColor
  280.      * @exception PropertyVetoException
  281.      * if the specified property value is unacceptable
  282.      */
  283.     public void setTextColor(Color color) throws PropertyVetoException
  284.     {
  285.         if(! symantec.itools.util.GeneralUtils.objectsEqual(textColor, color))
  286.         {
  287.             Color oldValue = textColor;
  288.  
  289.             vetos.fireVetoableChange("textColor", oldValue, color);
  290.  
  291.             if (textAnimationThread != null)
  292.                 textAnimationThread.suspend();
  293.  
  294.             textColor = color;
  295.  
  296.             repaint();
  297.  
  298.             if (textAnimationThread != null)
  299.                 textAnimationThread.resume();
  300.  
  301.             changes.firePropertyChange("textColor", oldValue, color);
  302.         }
  303.     }
  304.  
  305.     /**
  306.      * Gets the current text color.
  307.      * @return the current color of the text
  308.      * @see #setTextColor
  309.      */
  310.     public Color getTextColor()
  311.     {
  312.         return textColor;
  313.     }
  314.  
  315.  
  316.     /**
  317.      * Sets the current highlight color which will be used if highlightText
  318.      * is true.
  319.      *
  320.      * @param color the new highlight color
  321.      * @exception PropertyVetoException
  322.      * if the specified property value is unacceptable
  323.      * @see #getTextHighlightColor
  324.      * @see #getHighlightText
  325.      * @see #setHighlightText
  326.      */
  327.     public void setTextHighlightColor(Color color) throws PropertyVetoException
  328.     {
  329.         if (!GeneralUtils.objectsEqual(textHighlightColor, color))
  330.         {
  331.             Color oldValue = textHighlightColor;
  332.  
  333.             vetos.fireVetoableChange("textHighlightColor", oldValue, color);
  334.  
  335.             if (textAnimationThread != null)
  336.                 textAnimationThread.suspend();
  337.  
  338.             textHighlightColor = color;
  339.  
  340.             if (highlightText)
  341.                 repaint();
  342.  
  343.             if (textAnimationThread != null)
  344.                 textAnimationThread.resume();
  345.  
  346.             changes.firePropertyChange("textHighlightColor", oldValue, color);
  347.         }
  348.     }
  349.  
  350.  
  351.     /**
  352.      * Gets the current highlight color which will be used if highlightText
  353.      * is true.
  354.      *
  355.      * @return the current highlight color
  356.      * @see #setTextHighlightColor
  357.      * @see #getHighlightText
  358.      * @see #setHighlightText
  359.      */
  360.     public Color getTextHighlightColor()
  361.     {
  362.         return textHighlightColor;
  363.     }
  364.  
  365.  
  366.     /**
  367.      * Sets the "Highlight Text" property which determines whether the text
  368.      * will be highlighted with the current textHighlightColor or not.
  369.      *
  370.      * @param newValue the new value of this property
  371.      * @exception PropertyVetoException
  372.      * if the specified property value is unacceptable
  373.      * @see #getHighlightText
  374.      * @see #getTextHighlightColor
  375.      * @see #setTextHighlightColor
  376.      */
  377.     public void setHighlightText(boolean newValue) throws PropertyVetoException
  378.     {
  379.         if (highlightText != newValue)
  380.         {
  381.             stopScrolling();
  382.  
  383.             Boolean oldHighlightText = new Boolean(highlightText);
  384.             Boolean newHighlightText = new Boolean(newValue);
  385.  
  386.             vetos.fireVetoableChange("highlightText", oldHighlightText, newHighlightText);
  387.  
  388.             highlightText = newValue;
  389.  
  390.             extraHighlightWidth = (highlightText ? 6 : 0);
  391.             extraHighlightHeight = (highlightText ? 4 : 0);
  392.  
  393.             changes.firePropertyChange("highlightText", oldHighlightText, newHighlightText);
  394.         }
  395.     }
  396.  
  397.  
  398.     /**
  399.      * Gets the current value of the "Highlight Text" property which
  400.      * determines whether the text will be highlighted with the current
  401.      * textHighlightColor or not.
  402.      *
  403.      * @return the current value of this property
  404.      * @see #setHighlightText
  405.      * @see #getTextHighlightColor
  406.      * @see #setTextHighlightColor
  407.      */
  408.     public boolean getHighlightText()
  409.     {
  410.         return highlightText;
  411.     }
  412.  
  413.     /**
  414.      * Reshapes the Component to the specified bounding box.
  415.      *
  416.      * It has been OVERRIDDEN here to adjust all the animation buffers
  417.      * to the correct size (based on the new size of the InfoTipPanel)
  418.      *
  419.      * @param x the x coordinate
  420.      * @param y the y coordinate
  421.      * @param width the width of the component
  422.      * @param height the height of the component
  423.      * @see java.awt.Component#getBounds
  424.      * @see java.awt.Component#setLocation
  425.      * @see java.awt.Component#setSize
  426.      */
  427.     public void setBounds(int x, int y, int width, int height)
  428.     {
  429.         stopScrolling();
  430.  
  431.         super.setBounds(x, y, width, height);
  432.  
  433.         createOffScreenBuffers();
  434.     }
  435.  
  436.     /**
  437.      * Sets the URL of the image to display in this panel.
  438.      *
  439.      * It has been OVERRIDDEN here to suspend the animation thread while the image
  440.      * is set, and to re-buffer the new background.
  441.      *
  442.      * @param url the URL of the image to display
  443.      * @see symantec.itools.awt.ImagePanel#setImageURL
  444.      * @see symantec.itools.awt.ImagePanel#getImageURL
  445.      * @exception PropertyVetoException
  446.      * if the specified property value is unacceptable
  447.      */
  448.     public void setImageURL(URL url) throws PropertyVetoException
  449.     {
  450.         if (textAnimationThread != null)
  451.             textAnimationThread.suspend();
  452.  
  453.         super.setImageURL(url);
  454.         storeBackground();
  455.  
  456.         if (textAnimationThread != null)
  457.             textAnimationThread.resume();
  458.     }
  459.  
  460.     /**
  461.      * Sets the new panel image style.
  462.      *
  463.      * It has been OVERRIDDEN here to suspend the animation thread while the style
  464.      * is set, and to re-buffer the new background.
  465.      *
  466.      * @param newStyle the new panel image style, one of
  467.      * IMAGE_TILED, IMAGE_CENTERED, or IMAGE_SCALED_TO_FIT
  468.      * @exception PropertyVetoException
  469.      * if the specified property value is unacceptable
  470.      * @see symantec.itools.awt.ImagePanel#setStyle
  471.      * @see symantec.itools.awt.ImagePanel#getStyle
  472.      * @see symantec.itools.awt.ImagePanel#IMAGE_TILED
  473.      * @see symantec.itools.awt.ImagePanel#IMAGE_CENTERED
  474.      * @see symantec.itools.awt.ImagePanel#IMAGE_SCALED_TO_FIT
  475.      */
  476.     public void setStyle(int newStyle) throws PropertyVetoException
  477.     {
  478.         if (textAnimationThread != null)
  479.             textAnimationThread.suspend();
  480.  
  481.         super.setStyle(newStyle);
  482.         storeBackground();
  483.  
  484.         if (textAnimationThread != null)
  485.             textAnimationThread.resume();
  486.     }
  487.  
  488.     /**
  489.      * Sets the component in this InfoTipPanel. This is the component
  490.      * that triggers text scrolling on a mouse entered event. The InfoTipPanel
  491.      * can only contain one component.  Any previous component will be
  492.      * removed before the new one is added.
  493.      *
  494.      * @param comp the component to add
  495.      * @see #getComponent
  496.      */
  497.     public void setComponent(Component comp)
  498.     {
  499.         removeAll();    //we rely on the fact that removeAll() calls stopScrolling()
  500.  
  501.         myComponent = comp;
  502.  
  503.         //Add the new component at the end
  504.         callingAddInternally = true;
  505.         try
  506.         {
  507.             if (comp != null)
  508.                 super.add(myComponent);
  509.         }
  510.         finally
  511.         {
  512.             callingAddInternally = false;
  513.         }
  514.  
  515.         createOffScreenBuffers();
  516.  
  517.         if (myComponent != null)
  518.             myComponent.addMouseListener(myMouseListener);
  519.     }
  520.  
  521.     /**
  522.      * Gets the current component in the InfoTipPanel.
  523.      * This is the component that triggers text scrolling on a
  524.      * mouse entered event.
  525.      * @return the current component in the InfoTipPanel
  526.      * @see #setComponent
  527.      */
  528.     public Component getComponent()
  529.     {
  530.         return myComponent;
  531.     }
  532.  
  533.     /**
  534.      * Sets the font of the component.
  535.      * This is a standard method of java.awt.component.
  536.      *
  537.      * It has been OVERRIDDEN here to recalculate the sizes/locations
  538.      * of everything based on the new font.
  539.      *
  540.      * @param f the font
  541.      */
  542.     public synchronized void setFont(Font f)
  543.     {
  544.         stopScrolling();
  545.         super.setFont(f);
  546.  
  547.         calculateTextAttributes();
  548.         createOffScreenBuffers();
  549.     }
  550.  
  551.     /**
  552.      * Returns the recommended dimensions to properly display this component.
  553.      * This is a standard Java AWT method which gets called to determine
  554.      * the recommended size of this component.
  555.      *
  556.      * It has been OVERRIDEEN here to, for each axis, return the larger of
  557.      * the current size and the size of myComponent.
  558.      *
  559.      * @return A Dimension representing the preferred size of this component
  560.      * @see #getMinimumSize
  561.      */
  562.     public Dimension getPreferredSize()
  563.     {
  564.         int preferredWidth = Math.max(getSize().width, myComponent == null ? 0 : myComponent.getSize().width);
  565.         int preferredHeight = Math.max(getSize().height, myComponent == null ? 0 : myComponent.getSize().height);
  566.  
  567.         return new Dimension(preferredWidth, preferredHeight);
  568.     }
  569.  
  570.     /**
  571.      * Returns the minimum dimensions to properly display this component.
  572.      * This is a standard Java AWT method which gets called to determine
  573.      * the minimum size of this component.
  574.      *
  575.      * It is OVERRIDDEN here to just return the preferred size as determined
  576.      * by a call to getPreferredSize()
  577.      *
  578.      * @return A Dimension representing the minimum size of this component
  579.      * @see #getPreferredSize
  580.      */
  581.     public Dimension getMinimumSize()
  582.     {
  583.             return getPreferredSize();
  584.     }
  585.  
  586.     /**
  587.      * Takes no action.
  588.      * This is a standard Java AWT method which gets called to specify
  589.      * which layout manager should be used to layout the components in
  590.      * standard containers.
  591.      *
  592.      * Since layout managers CANNOT BE USED with this container the standard
  593.      * setLayout has been OVERRIDDEN for this container and does nothing.
  594.      *
  595.      * @param l the layout manager to use to layout this container's components
  596.      * (IGNORED)
  597.      * @see java.awt.Container#getLayout
  598.      **/
  599.     public void setLayout(LayoutManager mgr)
  600.     {
  601.     }
  602.  
  603.     /**
  604.      * Paints this component using the given graphics context.
  605.      * This is a standard Java AWT method which typically gets called
  606.      * by the AWT to handle painting this component. It paints this component
  607.      * using the given graphics context. The graphics context clipping region
  608.      * is set to the bounding rectangle of this component and its [0,0]
  609.      * coordinate is this component's top-left corner.
  610.      *
  611.      * It has been OVERRIDDEN for this container to call super.paint(g), and
  612.      * then draw it's text, if necessary.
  613.      *
  614.      * @param g the graphics context used for painting
  615.      * @see java.awt.Component#repaint
  616.      */
  617.     public void paint(Graphics g)
  618.     {
  619.         super.paint(g);
  620.  
  621.         if (!dontDrawText)
  622.         {
  623.             if (scrollState == DONE_SCROLLING_IN)
  624.             {
  625.                 //If the text is supposed to be scrolled in all the way, we must draw it.
  626.                 //Note that if the text is in the process of scrolling in or out the animation
  627.                 //thread will handle drawing it at the appropriate time.
  628.                 Point textPosition = getTextFinishingPoint();
  629.                 drawText(g, textPosition.x, textPosition.y);
  630.             }
  631.         }
  632.     }
  633.  
  634.     /**
  635.      * Tells this component that it has been added to a container.
  636.      * This is a standard Java AWT method which gets called by the AWT when
  637.      * this component is added to a container. Typically, it is used to
  638.      * create this component's peer.
  639.      *
  640.      * It has been OVERRIDDEN here to calculate text sizes and to create
  641.      * any off screen buffers that will be needed for the animation.
  642.      *
  643.      * @see #removeNotify
  644.      */
  645.     public synchronized void addNotify()
  646.     {
  647.         super.addNotify();
  648.         
  649.         try
  650.         {
  651.             errors = ResourceBundle.getBundle("symantec.itools.resources.ErrorsBundle");
  652.         }
  653.         catch(Throwable ex)
  654.         {
  655.             errors = new symantec.itools.resources.ErrorsBundle();
  656.         }
  657.  
  658.         //re-calculate sizes and re-create off screen images now that they have a graphics context
  659.         calculateTextAttributes();
  660.         createOffScreenBuffers();
  661.  
  662.         locationVeto = new LocationVeto();
  663.         addTextLocationListener(locationVeto);
  664.     }
  665.  
  666.     /**
  667.      * Tells this component that it is being removed from a container.
  668.      * This is a standard Java AWT method which gets called by the AWT when
  669.      * this component is removed from a container. Typically, it is used to
  670.      * destroy the peers of this component and all its subcomponents.
  671.      *
  672.      * It has been OVERRIDDEN here to dispose of any buffers or any
  673.      * animation thread in use by this InfoTipPanel, and then
  674.      * call super.removeNotify().
  675.      *
  676.      * @see #addNotify
  677.      */
  678.     public synchronized void removeNotify()
  679.     {
  680.         stopScrolling();
  681.  
  682.         if (offScreenImage != null)
  683.         {
  684.             offScreenImage.flush();
  685.             offScreenImage = null;
  686.         }
  687.  
  688.         if (bufferedBgndImage != null)
  689.         {
  690.             bufferedBgndImage.flush();
  691.             bufferedBgndImage = null;
  692.         }
  693.  
  694.         removeTextLocationListener(locationVeto);
  695.         locationVeto = null;
  696.  
  697.         super.removeNotify();
  698.     }
  699.  
  700.     /**
  701.      * Standard method in the java.lang.Runnable interface.
  702.      * When an object implementing interface <code>Runnable</code> is used
  703.      * to create a thread, starting the thread causes the object's
  704.      * <code>run</code> method to be called in that separately executing
  705.      * thread.
  706.      *
  707.      * Here, run has been implemented to do animate the text scrolling in and out.
  708.      * The thread calling this method will continue to animate the text until it
  709.      * scrolls either completely out, or completely in.
  710.      *
  711.      * @see     java.lang.Thread#run
  712.      * @since   JDK1.0
  713.      */
  714.     public void run()
  715.     {
  716.         if (text != null && validateOffScreenBuffers())
  717.         {
  718.             switch (scrollState) {
  719.                 case SCROLLING_OUT:
  720.                     if (textScrollsIn)
  721.                         scrollText();
  722.                     else
  723.                         popDownText();
  724.                     break;
  725.                 case SCROLLING_IN:
  726.                     if (textScrollsIn)
  727.                         scrollText();
  728.                     else
  729.                         popUpText();
  730.                     break;
  731.             }
  732.         }
  733.  
  734.         textAnimationThread = null;
  735.     }
  736.  
  737.     /**
  738.      * Removes the specified component from this container.
  739.      * This is a standard Java AWT method which gets called to remove a
  740.      * component from a container. When this happens the component's
  741.      * removeNotify() will also get called to indicate component removal.
  742.      *
  743.      * It has been OVERRIDDEN here to add remove the listeners which we added
  744.      * to the component.
  745.      *
  746.      * @param comp the component to remove
  747.      * @see #removeAll
  748.      * @see java.awt.Container#add
  749.      */
  750.     public synchronized void remove(Component comp)
  751.     {
  752.         stopScrolling();
  753.  
  754.         super.remove(comp);
  755.  
  756.         if (comp == myComponent)
  757.         {
  758.             myComponent.removeMouseListener(myMouseListener);
  759.             myComponent = null;
  760.         }
  761.     }
  762.  
  763.     /**
  764.      * Removes all the components from this container.
  765.      * This is a standard Java AWT method which gets called to remove all
  766.      * the components from a container. When this happens each component's
  767.      * removeNotify() will also get called to indicate component removal.
  768.      *
  769.      * It has been OVERRIDDEN here to remove the InfoTipPanel's
  770.      * reference to its component.
  771.      *
  772.      * @see #remove
  773.      * @see java.awt.Container#add
  774.      */
  775.     public synchronized void removeAll()
  776.     {
  777.         stopScrolling();
  778.  
  779.         if (this.myComponent != null)
  780.             super.remove(this.myComponent);
  781.  
  782.         myComponent = null;
  783.     }
  784.  
  785.     /**
  786.      * Adds a listener for all event changes.
  787.      * @param listener the listener to add.
  788.      * @see #removePropertyChangeListener
  789.      */
  790.     public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
  791.     {
  792.         super.addPropertyChangeListener(listener);
  793.         changes.addPropertyChangeListener(listener);
  794.     }
  795.  
  796.     /**
  797.      * Removes a listener for all event changes.
  798.      * @param listener the listener to remove.
  799.      * @see #addPropertyChangeListener
  800.      */
  801.     public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
  802.     {
  803.         super.removePropertyChangeListener(listener);
  804.         changes.removePropertyChangeListener(listener);
  805.     }
  806.  
  807.     /**
  808.      * Adds a vetoable listener for all event changes.
  809.      * @param listener the listener to add.
  810.      * @see #removeVetoableChangeListener
  811.      */
  812.     public synchronized void addVetoableChangeListener(VetoableChangeListener listener)
  813.     {
  814.         super.addVetoableChangeListener(listener);
  815.         vetos.addVetoableChangeListener(listener);
  816.     }
  817.  
  818.     /**
  819.      * Removes a vetoable listener for all event changes.
  820.      * @param listener the listener to remove.
  821.      * @see #addVetoableChangeListener
  822.      */
  823.     public synchronized void removeVetoableChangeListener(VetoableChangeListener listener)
  824.     {
  825.         super.removeVetoableChangeListener(listener);
  826.         vetos.removeVetoableChangeListener(listener);
  827.     }
  828.  
  829.     /**
  830.      * Adds a listener for the TextLocation property changes.
  831.      * @param listener the listener to add.
  832.      * @see #removeTextLocationListener(java.beans.PropertyChangeListener)
  833.      */
  834.     public synchronized void addTextLocationListener(PropertyChangeListener listener)
  835.     {
  836.         changes.addPropertyChangeListener("textLocation", listener);
  837.     }
  838.  
  839.     /**
  840.      * Removes a listener for the TextLocation property changes.
  841.      * @param listener the listener to remove.
  842.      * @see #addTextLocationListener(java.beans.PropertyChangeListener)
  843.      */
  844.     public synchronized void removeTextLocationListener(PropertyChangeListener listener)
  845.     {
  846.         changes.removePropertyChangeListener("textLocation", listener);
  847.     }
  848.  
  849.     /**
  850.      * Adds a vetoable listener for the TextLocation property changes.
  851.      * @param listener the listener to add.
  852.      * @see #removeTextLocationListener(java.beans.VetoableChangeListener)
  853.      */
  854.     public synchronized void addTextLocationListener(VetoableChangeListener listener)
  855.     {
  856.         vetos.addVetoableChangeListener("textLocation", listener);
  857.     }
  858.  
  859.     /**
  860.      * Removes a vetoable listener for the TextLocation property changes.
  861.      * @param listener the listener to remove.
  862.      * @see #addTextLocationListener(java.beans.VetoableChangeListener)
  863.      */
  864.     public synchronized void removeTextLocationListener(VetoableChangeListener listener)
  865.     {
  866.         vetos.removeVetoableChangeListener("textLocation", listener);
  867.     }
  868.  
  869.     /**
  870.      * Implementation of the MouseListener interface so that we can trigger
  871.      * text scrolling on mouseEntered() and mouseExited().
  872.      */
  873.     class Mouse extends MouseAdapter
  874.     {
  875.         public void mouseEntered(MouseEvent e)
  876.         {
  877.             startScrolling(SCROLLING_IN, false);
  878.         }
  879.  
  880.         public void mouseExited(MouseEvent e)
  881.         {
  882.             startScrolling(SCROLLING_OUT, false);
  883.         }
  884.     }
  885.  
  886.     /**
  887.      * Implementation of the ActionListener interface so that we can trigger
  888.      * text scrolling after a delay.
  889.      */
  890.     class Action implements ActionListener
  891.     {
  892.         public void actionPerformed(ActionEvent e)
  893.         {
  894.             startScrolling(SCROLLING_IN, true);  //true so that we don't start another timer and wait again
  895.         }
  896.     }
  897.  
  898.  
  899.     /**
  900.      * This is the PropertyChangeEvent handling inner class for the constrained TextLocation property.
  901.      * Handles vetoing TextLocations that are outside of the valid range.
  902.      */
  903.     class LocationVeto implements java.beans.VetoableChangeListener, java.io.Serializable
  904.     {
  905.         /**
  906.          * This method gets called when an attempt to change the constrained TextLocation property is made.
  907.          * Ensures the given text location is valid.
  908.          *
  909.          * @param     e a <code>PropertyChangeEvent</code> object describing the
  910.          *             event source and the property that has changed.
  911.          * @exception PropertyVetoException if the recipient wishes the property
  912.          *              change to be rolled back.
  913.          */
  914.         public void vetoableChange(PropertyChangeEvent e) throws PropertyVetoException
  915.         {
  916.             int i = ((Integer)e.getNewValue()).intValue();
  917.             if (!isValidTextLocation(i))
  918.             {
  919.                 throw new PropertyVetoException(errors.getString("InvalidTextLocation") + i, e);
  920.             }
  921.         }
  922.     }
  923.  
  924.     /**
  925.      * Is the given text location valid.
  926.      * @param i the given text location
  927.      * @return true if the given text location is acceptable, false if not.
  928.      */
  929.     protected boolean isValidTextLocation(int i)
  930.     {
  931.         switch(i)
  932.         {
  933.             case RIGHT_OF_COMPONENT:
  934.             case LEFT_OF_COMPONENT:
  935.             case ABOVE_COMPONENT:
  936.             case BELOW_COMPONENT:
  937.                 return true;
  938.             default:
  939.                 return false;
  940.         }
  941.     }
  942.  
  943.  
  944.     /**
  945.      * Draws the current text into the graphics context and location specified
  946.      * in the parameters.  Highlights the text, if necessary.
  947.      *
  948.      * @param g the graphics context into which the text should be drawn
  949.      * @param x the x-coordinate at which the text should be drawn
  950.      * @param y the y-coordinate at which the text should be drawn
  951.      */
  952.     protected void drawText(Graphics g, int x, int y)
  953.     {
  954.         Color savedColor = g.getColor();
  955.  
  956.         if (highlightText)
  957.         {
  958.             g.setColor(textHighlightColor);
  959.  
  960.             Rectangle highlightBounds = getHighlightBounds(x, y);
  961.             if (highlightBounds != null)
  962.                 g.fillRect(highlightBounds.x, highlightBounds.y, highlightBounds.width, highlightBounds.height);
  963.  
  964.             g.setColor(textColor);
  965.  
  966.             g.drawRect(highlightBounds.x,
  967.                         highlightBounds.y,
  968.                         highlightBounds.width - 1,
  969.                         highlightBounds.height - 1);
  970.                     // -1 so that we fit the rectangle within the bounds
  971.         }
  972.  
  973.         g.setColor(textColor);
  974.         g.drawString(text, x, y);
  975.         g.setColor(savedColor);
  976.     }
  977.  
  978.     /**
  979.      * Determines the rectangle which bounds the area that should be highlighted.
  980.      *
  981.      * @return this bounding Rectangle, or null if textWidth or textHeight is zero.
  982.      */
  983.     protected Rectangle getHighlightBounds(int textX, int textY)
  984.     {
  985.         if (textWidth > 0 && textHeight > 0)
  986.             return new Rectangle(textX - extraHighlightWidth/2,
  987.                                     textY - textAscent - extraHighlightHeight/2,
  988.                                     textWidth + extraHighlightWidth,
  989.                                     textHeight + extraHighlightHeight);
  990.         else
  991.             return null;
  992.     }
  993.  
  994.     /**
  995.      * Starts the text scrolling in the direction indicated by the parameter
  996.      * newScrollState.  The text will continue to be scrolled back and
  997.      * forth until the text scrolls all the way in, or all the way out.
  998.      * Causes a thread to be started to animate the text.
  999.      *
  1000.      * This method is also responsible for starting the timer if the textDelay
  1001.      * is set to some number of milliseconds.  This method will be called a
  1002.      * second time if and when that timer goes off.
  1003.      *
  1004.      * @param newScrollState the direction of the scroll: SCROLLING_IN,
  1005.      * or SCROLLING_OUT
  1006.      * @param ignoreDelay whether or not to ignore a delay even if it would normally
  1007.      * be necessary (i.e. even though the text is starting to scroll in)
  1008.      * @see #stopScrolling
  1009.      */
  1010.     protected void startScrolling(int newScrollState, boolean ignoreDelay)
  1011.     {
  1012.         delayTimer.stop();
  1013.  
  1014.  
  1015.         if ((scrollState == DONE_SCROLLING_OUT && newScrollState == SCROLLING_IN)
  1016.             || (scrollState == WAITING_TO_SCROLL_IN && newScrollState == SCROLLING_IN))
  1017.         {
  1018.             //Start scrolling in!
  1019.             //Start the animation or the timer, whichever is appropriate
  1020.             if (ignoreDelay || textDelay == 0)
  1021.             {
  1022.                 scrollState = newScrollState;
  1023.  
  1024.                 textAnimationThread = new Thread(this);
  1025.                 textAnimationThread.start();
  1026.             }
  1027.             else
  1028.             {
  1029.                 scrollState = WAITING_TO_SCROLL_IN;
  1030.                 delayTimer.start();
  1031.             }
  1032.         }
  1033.         else if (scrollState == DONE_SCROLLING_IN && newScrollState == SCROLLING_OUT)
  1034.         {
  1035.             //Start scrolling out!
  1036.             scrollState = newScrollState;
  1037.  
  1038.             textAnimationThread = new Thread(this);
  1039.             textAnimationThread.start();
  1040.         }
  1041.         else if (scrollState == WAITING_TO_SCROLL_IN && newScrollState == SCROLLING_OUT)
  1042.         {
  1043.             //We never actually started the scrolling in animation before this mouseExited event
  1044.             //occurred, so all we have to do is set the scrollState to DONE_SCROLLING_OUT
  1045.             scrollState = DONE_SCROLLING_OUT;
  1046.         }
  1047.         else
  1048.         {
  1049.             //We should already be in the process of scrolling text one way or the other.  Just
  1050.             //update scrollState, and the animation thread will take care of the rest.
  1051.             scrollState = newScrollState;
  1052.         }
  1053.     }
  1054.  
  1055.     /**
  1056.      * Stops the scrolling text animation (if any).  Any subsequent mouse
  1057.      * over events will cause the text to scroll again, as usual.
  1058.      *
  1059.      * @see #startScrolling
  1060.      */
  1061.     protected void stopScrolling()
  1062.     {
  1063.         delayTimer.stop();    //these two lines stop any pending scrolls
  1064.  
  1065.         if (scrollState != DONE_SCROLLING_IN)
  1066.             scrollState = DONE_SCROLLING_OUT;    //It's OK if the text is already done scrolling in.  Otherwise,
  1067.                                                 //set the state to DONE_SCROLLING_OUT
  1068.         if (textAnimationThread != null)
  1069.         {
  1070.             textAnimationThread.stop();
  1071.             textAnimationThread = null;
  1072.             repaint();  //repaint to get rid of text which may be partially scrolled in
  1073.         }
  1074.     }
  1075.  
  1076.     /**
  1077.      * Draws the text next to the component (in the location specified
  1078.      * by textLocation).
  1079.      *
  1080.      * @see #popDownText
  1081.      */
  1082.     protected void popUpText()
  1083.     {
  1084.         //with this one call we draw the text, and also update the scrollState.
  1085.         drawNextFrame(getTextFinishingPoint());
  1086.     }
  1087.  
  1088.     /**
  1089.      * Basically just erases the text next to the component (in the location specified
  1090.      * by textLocation).
  1091.      *
  1092.      * @see #popUpText
  1093.      */
  1094.     protected void popDownText()
  1095.     {
  1096.         Graphics g = getGraphics();
  1097.  
  1098.         if (g != null)
  1099.         {
  1100.             Rectangle bufferedBgndImageBounds = getBufferedBackgroundBounds();
  1101.  
  1102.             if (bufferedBgndImageBounds != null)
  1103.             {
  1104.                 //just paint our buffered background
  1105.                 g.drawImage(bufferedBgndImage, bufferedBgndImageBounds.x, bufferedBgndImageBounds.y, this);
  1106.  
  1107.                 //all done!  Set our scrollState accordingly.
  1108.                 scrollState = DONE_SCROLLING_OUT;
  1109.             }
  1110.         }
  1111.     }
  1112.  
  1113.     /**
  1114.      * Start the text scrolling in or out from the appropriate location.
  1115.      * The text will continue to be scrolled back and forth until the text
  1116.      * scrolls all the way in, or all the way out.
  1117.      */
  1118.     protected void scrollText()
  1119.     {
  1120.         Point currentTextLocation;
  1121.         if (scrollState == SCROLLING_IN)
  1122.             currentTextLocation = getTextStartingPoint();
  1123.         else
  1124.             currentTextLocation = getTextFinishingPoint();
  1125.  
  1126.         while (scrollState != DONE_SCROLLING_IN && scrollState != DONE_SCROLLING_OUT)
  1127.             currentTextLocation = scrollOneDirection(currentTextLocation);
  1128.     }
  1129.  
  1130.     /**
  1131.      * Utility method of run
  1132.      *
  1133.      * This method checks to see whether the off screen images necessary for
  1134.      * the animation already exist, and if so that they are of the correct size.
  1135.      * If the buffers do not exist this method tries to create them.
  1136.      *
  1137.      * Due to a bug in some VMs componentResize events may not get sent correctly
  1138.      * so we must also check that the sizes of the buffers are correct.
  1139.      *
  1140.      * @return a boolean indicating whether or not buffers exist by the end of
  1141.      * this method which are sufficient for the animation.
  1142.      * @see #run
  1143.      */
  1144.     protected boolean validateOffScreenBuffers()
  1145.     {
  1146.  
  1147.         if (offScreenImage == null || bufferedBgndImage == null)
  1148.         {
  1149.             createOffScreenBuffers();
  1150.         }
  1151.         else
  1152.         {
  1153.             //Since component resize events are not sent correctly on some VMs,
  1154.             //we must check to see if the component size is in sync with the
  1155.             //off screen buffer sizes.  Once this works in all VMs we can accomplish
  1156.             //this through listening to component resize events.
  1157.  
  1158.             Rectangle correctBounds = getBufferedBackgroundBounds();
  1159.  
  1160.             if (correctBounds != null)
  1161.             {
  1162.                 if (offScreenImage.getWidth(this) != correctBounds.width
  1163.                     || offScreenImage.getHeight(this) != correctBounds.height)
  1164.                 {
  1165.                     createOffScreenBuffers();
  1166.                 }
  1167.             }
  1168.         }
  1169.  
  1170.         return (offScreenImage != null && bufferedBgndImage != null);
  1171.     }
  1172.  
  1173.     /**
  1174.      * Utility method for run.
  1175.      *
  1176.      * This method stores the background (in this case only the panel's image
  1177.      * since this is a heavyweight component) into bufferedBgndImage.
  1178.      * Only the portion of the background inside of the bounds specified by
  1179.      * getBufferedBackgroundBounds() is saved.
  1180.      *
  1181.      * @see #run
  1182.      */
  1183.     protected void storeBackground()
  1184.     {
  1185.         if (bufferedBgndImage != null)
  1186.         {
  1187.             Graphics buffer = bufferedBgndImage.getGraphics();
  1188.             Rectangle bufferBounds = getBufferedBackgroundBounds();
  1189.  
  1190.             if (buffer != null && bufferBounds != null)
  1191.             {
  1192.                 buffer.translate(-bufferBounds.x, -bufferBounds.y);
  1193.  
  1194.                 dontDrawText = true;
  1195.                 try
  1196.                 {
  1197.                     paint(buffer);
  1198.                 }
  1199.                 finally
  1200.                 {
  1201.                     dontDrawText = false;
  1202.                 }
  1203.  
  1204.                 buffer.translate(bufferBounds.x, bufferBounds.y);
  1205.             }
  1206.         }
  1207.     }
  1208.  
  1209.     /**
  1210.      * Starts the text scrolling in the current direction, starting from
  1211.      * the point specified by the parameter startingPoint.
  1212.      * The text will continue to be scrolled until the current direction
  1213.      * changes (due to a mouse event, or due to the text scrolling all
  1214.      * the way in or out).
  1215.      *
  1216.      * @param startingPoint the point from which the text starts scrolling
  1217.      * @return the point at which the text stopped scrolling
  1218.      * @see #TIME_BETWEEN_FRAMES
  1219.      */
  1220.     protected Point scrollOneDirection(Point startingPoint)
  1221.     {
  1222.         Point currentPoint = startingPoint;
  1223.         int currentDirection = scrollState;
  1224.  
  1225.         while(scrollState == currentDirection)
  1226.         {
  1227.             try
  1228.             {
  1229.                 textAnimationThread.sleep(TIME_BETWEEN_FRAMES);
  1230.             }
  1231.             catch (InterruptedException e) {}
  1232.  
  1233.             drawNextFrame(currentPoint);
  1234.         }
  1235.  
  1236.         return currentPoint;
  1237.     }
  1238.  
  1239.     /**
  1240.      * Does all necessary calculations and graphics work to advance the text one frame
  1241.      * (starting from the point specified in the paramter startingPoint).
  1242.      *
  1243.      * statingPoint is modified by this method to be the NEW position of the text (scrolled
  1244.      * one frame further than when the method was called)
  1245.      *
  1246.      * @param startingPoint the current position of the text (before this method advances it
  1247.      * by one animation frame).
  1248.         */
  1249.     protected void drawNextFrame(Point startingPoint)
  1250.     {
  1251.         Graphics g = getGraphics();
  1252.  
  1253.         if (g != null)
  1254.         {
  1255.             Graphics offScreenGraphics = offScreenImage.getGraphics();
  1256.  
  1257.             Rectangle bufferedBgndImageBounds = getBufferedBackgroundBounds();
  1258.  
  1259.             if (bufferedBgndImageBounds != null)
  1260.             {
  1261.                 //first, paint our buffered background
  1262.                 offScreenGraphics.drawImage(bufferedBgndImage, 0, 0, this);
  1263.  
  1264.                 //now, paint our text on top of that
  1265.                 startingPoint.x += calculateDX(startingPoint);
  1266.                 startingPoint.y += calculateDY(startingPoint);
  1267.  
  1268.                 drawText(offScreenGraphics, startingPoint.x - bufferedBgndImageBounds.x, startingPoint.y - bufferedBgndImageBounds.y);
  1269.  
  1270.                 //take the resulting off screen image and paint it into our 'on-screen' graphics context
  1271.                 g.drawImage(offScreenImage, bufferedBgndImageBounds.x, bufferedBgndImageBounds.y, this);
  1272.  
  1273.                 //determine what direction we should be scrolling in now
  1274.                 updateDirection(startingPoint);
  1275.             }
  1276.         }
  1277.     }
  1278.  
  1279.     /**
  1280.      * Utility method for drawNextFrame.
  1281.      *
  1282.      * Given the current location of the text, this method calculates how far in the
  1283.      * x-direction the text should move when it advances to the next frame of the animation.
  1284.      *
  1285.      * @param startingPoint the current location of the text.
  1286.      * @return the distance in the x-direction the text should move when it advances
  1287.      * to the next frame of the animation.
  1288.      * @see #calculateDY
  1289.      */
  1290.     protected int calculateDX(Point startingPoint)
  1291.     {
  1292.         int dx = 0;
  1293.         Point destination = new Point();
  1294.  
  1295.         if (textLocation == ABOVE_COMPONENT || textLocation == BELOW_COMPONENT)
  1296.             return 0;
  1297.  
  1298.         switch (scrollState)
  1299.         {
  1300.             case SCROLLING_IN:
  1301.                 destination = getTextFinishingPoint();
  1302.  
  1303.                 if (atOrBeyondPoint(startingPoint, destination, DX_PER_FRAME))
  1304.                     dx = -(startingPoint.x - destination.x);
  1305.                  else
  1306.                     dx = (textLocation == LEFT_OF_COMPONENT ? DX_PER_FRAME : -DX_PER_FRAME);
  1307.                 break;
  1308.             case SCROLLING_OUT:
  1309.                 destination = getTextStartingPoint();
  1310.  
  1311.                 if (atOrBeyondPoint(startingPoint, destination, DX_PER_FRAME))
  1312.                     dx = (destination.x - startingPoint.x);
  1313.                 else
  1314.                     dx = (textLocation == LEFT_OF_COMPONENT ? -DX_PER_FRAME : DX_PER_FRAME);
  1315.                 break;
  1316.             case DONE_SCROLLING_OUT:
  1317.             case DONE_SCROLLING_IN:
  1318.                 dx = 0;
  1319.                 break;
  1320.         }
  1321.         return dx;
  1322.     }
  1323.  
  1324.     /**
  1325.      * Utility method for drawNextFrame.
  1326.      *
  1327.      * Given the current location of the text, this method calculates how far in the
  1328.      * y-direction the text should move when it advances to the next frame of the animation.
  1329.      *
  1330.      * @param startingPoint the current location of the text.
  1331.      * @return the distance in the y-direction the text should move when it advances
  1332.      * to the next frame of the animation.
  1333.      * @see #calculateDX
  1334.      */
  1335.     protected int calculateDY(Point startingPoint)
  1336.     {
  1337.         int dy = 0;
  1338.         Point destination;
  1339.  
  1340.         if (textLocation == RIGHT_OF_COMPONENT || textLocation == LEFT_OF_COMPONENT)
  1341.             return 0;
  1342.  
  1343.         switch (scrollState)
  1344.         {
  1345.             case SCROLLING_IN:
  1346.                 destination = getTextFinishingPoint();
  1347.  
  1348.                 if (atOrBeyondPoint(startingPoint, destination, DY_PER_FRAME))
  1349.                     dy = -(startingPoint.y - destination.y);
  1350.                  else
  1351.                     dy = (textLocation == BELOW_COMPONENT ? -DY_PER_FRAME : DY_PER_FRAME);
  1352.                 break;
  1353.             case SCROLLING_OUT:
  1354.                 destination = getTextStartingPoint();
  1355.  
  1356.                 if (atOrBeyondPoint(startingPoint, destination, DY_PER_FRAME))
  1357.                     dy = (destination.y - startingPoint.y);
  1358.                 else
  1359.                     dy = (textLocation == BELOW_COMPONENT ? DY_PER_FRAME : -DY_PER_FRAME);
  1360.                 break;
  1361.             case DONE_SCROLLING_OUT:
  1362.             case DONE_SCROLLING_IN:
  1363.                 dy = 0;
  1364.                 break;
  1365.         }
  1366.         return dy;
  1367.     }
  1368.  
  1369.     /**
  1370.      * Utility method for drawNextFrame.
  1371.      *
  1372.      * Given the current location of the text, this method determines whether the
  1373.      * scrollState should be set to SCROLLING_IN, SCROLLING_OUT, DONE_SCROLLING_IN, or
  1374.      * DONE_SCROLLING_OUT.
  1375.      * This method actually modifies the 'scrollState' member of this class.
  1376.      *
  1377.      * @see #drawNextFrame
  1378.      * @see #scrollState
  1379.      */
  1380.     protected void updateDirection(Point currentPoint)
  1381.     {
  1382.         switch (scrollState)
  1383.         {
  1384.             case SCROLLING_IN:
  1385.                 if (atOrBeyondPoint(currentPoint, getTextFinishingPoint(), 0))
  1386.                     scrollState = DONE_SCROLLING_IN;
  1387.                 break;
  1388.             case SCROLLING_OUT:
  1389.                 if (atOrBeyondPoint(currentPoint, getTextStartingPoint(), 0))
  1390.                     scrollState = DONE_SCROLLING_OUT;
  1391.                 break;
  1392.             case DONE_SCROLLING_OUT:
  1393.             case DONE_SCROLLING_IN:
  1394.                 break;
  1395.         }
  1396.     }
  1397.  
  1398.     /**
  1399.      * Utility method for updateDirection, calculateDX, and calculateDY.
  1400.      *
  1401.      * If paramter 'allowance' equals zero, this method determines whether or not the text
  1402.      * has scrolled to a position which is at or beyond the point specified in the
  1403.      * parameter 'point' (based on textLocation and scrollState).
  1404.      *
  1405.      * For a non-zero 'allowance', this method determines whether or not the text
  1406.      * has scrolled to a position which is either beyond 'point' or within 'allowance'
  1407.      * pixels of 'point' (based on the current textLocation and scrollState).
  1408.      *
  1409.      * @return whether or not 'currentLocation' is at or beyond 'point' (given 'allowance' pixels of leeway).
  1410.      */
  1411.     protected boolean atOrBeyondPoint(Point currentLocation, Point point, int allowance)
  1412.     {
  1413.         if (currentLocation == null || point == null)
  1414.             return false;
  1415.  
  1416.         switch (textLocation)
  1417.         {
  1418.             case RIGHT_OF_COMPONENT:
  1419.                 switch (scrollState)
  1420.                 {
  1421.                      case SCROLLING_IN:
  1422.                         return currentLocation.x <= point.x + allowance;
  1423.                     case SCROLLING_OUT:
  1424.                         return currentLocation.x >= point.x - allowance;
  1425.                 }
  1426.             case LEFT_OF_COMPONENT:
  1427.                 switch (scrollState)
  1428.                 {
  1429.                      case SCROLLING_IN:
  1430.                         return currentLocation.x >= point.x - allowance;
  1431.                     case SCROLLING_OUT:
  1432.                         return currentLocation.x <= point.x + allowance;
  1433.                 }
  1434.             case ABOVE_COMPONENT:
  1435.                 switch (scrollState)
  1436.                 {
  1437.                      case SCROLLING_IN:
  1438.                         return currentLocation.y >= point.y - allowance;
  1439.                     case SCROLLING_OUT:
  1440.                         return currentLocation.y <= point.y + allowance;
  1441.                 }
  1442.             case BELOW_COMPONENT:
  1443.                 switch (scrollState)
  1444.                 {
  1445.                      case SCROLLING_IN:
  1446.                         return currentLocation.y <= point.y + allowance;
  1447.                     case SCROLLING_OUT:
  1448.                         return currentLocation.y >= point.y - allowance;
  1449.                 }
  1450.         }
  1451.         return false;
  1452.     }
  1453.  
  1454.     /**
  1455.      * Utility method for getTextStartingPoint and getTextFinishingPoint.
  1456.      *
  1457.      * This method calculates the y-position at which the text should be drawn if
  1458.      * the textLocation is either LEFT_OF_COMPONENT or RIGHT_OF_COMPONENT.  Requires
  1459.      * a graphics context to be available.
  1460.      *
  1461.      * @return the y-position at which the text should be drawn if
  1462.      * the textLocation is either LEFT_OF_COMPONENT or RIGHT_OF_COMPONENT.
  1463.      * @see #getTextStartingPoint
  1464.      * @see #getTextFinishingPoint
  1465.      */
  1466.     protected int getTextConstantYPos()
  1467.     {
  1468.         return myComponent.getLocation().y + (myComponent.getSize().height + textAscent)/2;
  1469.     }
  1470.  
  1471.     /**
  1472.      * Utility method for getTextStartingPoint and getTextFinishingPoint.
  1473.      *
  1474.      * This method calculates the x-position at which the text should be drawn if
  1475.      * the textLocation is either ABOVE_COMPONENT or BELOW_COMPONENT.  Requires
  1476.      * a graphics context to be available.
  1477.      *
  1478.      * @return the x-position at which the text should be drawn if
  1479.      * the textLocation is either ABOVE_COMPONENT or BELOW_COMPONENT.
  1480.      * @see #getTextStartingPoint
  1481.      * @see #getTextFinishingPoint
  1482.      */
  1483.     protected int getTextConstantXPos()
  1484.     {
  1485.         return myComponent.getLocation().x + (myComponent.getSize().width - textWidth) / 2;
  1486.     }
  1487.  
  1488.     /**
  1489.      * Utility method.
  1490.      *
  1491.      * This method calculates the ascent, descent, height, and width of the current
  1492.      * text, based on the current graphics context.
  1493.      *
  1494.      */
  1495.     protected void calculateTextAttributes()
  1496.     {
  1497.         if (text == null)
  1498.             return;
  1499.  
  1500.         Graphics g = getGraphics();
  1501.         if (g != null)
  1502.         {
  1503.             FontMetrics fm    = g.getFontMetrics();
  1504.             if (fm != null)
  1505.             {
  1506.                 textAscent = fm.getAscent();
  1507.                 textDescent = fm.getDescent();
  1508.                 textHeight = textAscent + textDescent;
  1509.                 textWidth = fm.stringWidth(text);
  1510.             }
  1511.         }
  1512.     }
  1513.  
  1514.     /**
  1515.      * Calculates the rectangle which bounds the portion of this component's background which
  1516.      * the text will animate over.
  1517.      *
  1518.      * @return this bounding rectangle.
  1519.      */
  1520.     protected Rectangle getBufferedBackgroundBounds()
  1521.     {
  1522.         if (myComponent != null)
  1523.         {
  1524.             if (textScrollsIn)
  1525.                 return getAreaAffectedByScroll();
  1526.             else
  1527.                 return getAreaAffectedByPopup();
  1528.         }
  1529.  
  1530.         //The component has not yet been set, so return null
  1531.         return null;
  1532.     }
  1533.  
  1534.     /**
  1535.      * Utility method for getBufferedBackgroundBounds.
  1536.      *
  1537.      * Calculates the rectangle which bounds the portion of this component's background
  1538.      * which the text will scroll over.
  1539.      *
  1540.      * @return this bounding rectangle (or null if there is insufficient space to do the
  1541.      * animation.
  1542.      */
  1543.     protected Rectangle getAreaAffectedByScroll()
  1544.     {
  1545.         int leftMostX = getBufferedBackgroundLeftMostX();
  1546.         int rightMostX = getBufferedBackgroundRightMostX();
  1547.  
  1548.         int topMostY = getBufferedBackgroundTopMostY();
  1549.         int bottomMostY = getBufferedBackgroundBottomMostY();
  1550.  
  1551.         if (rightMostX > leftMostX && topMostY < bottomMostY)
  1552.         {
  1553.             return new Rectangle(leftMostX - 1, topMostY - 1,
  1554.                                     rightMostX - leftMostX + 1, bottomMostY - topMostY + 1);
  1555.                                     // +1 and -1 to allow for drawing a rectangle around the text.
  1556.         }
  1557.  
  1558.         //The InfoTipPanel is not large enough for the user to
  1559.         //see any animation, so return null
  1560.         return null;
  1561.     }
  1562.  
  1563.     /**
  1564.      * Utility method for getBufferedBackgroundBounds.
  1565.      *
  1566.      * Calculates the rectangle which bounds the portion of this component's background
  1567.      * which the text will pop-up over.
  1568.      *
  1569.      * @return this bounding rectangle (or null if there is insufficient space to do the
  1570.      * animation.
  1571.      */
  1572.     protected Rectangle getAreaAffectedByPopup()
  1573.     {
  1574.         int leftMostX, rightMostX, topMostY, bottomMostY;
  1575.  
  1576.         if (textLocation == RIGHT_OF_COMPONENT)
  1577.             rightMostX = Math.min(getSize().width, getTextFinishingPoint().x + textWidth + (highlightText ? extraHighlightWidth/2 : 0));
  1578.         else
  1579.             rightMostX = getBufferedBackgroundRightMostX();
  1580.  
  1581.         if (textLocation == LEFT_OF_COMPONENT)
  1582.             leftMostX = Math.max(0, getTextFinishingPoint().x - (highlightText ? extraHighlightWidth/2 : 0));
  1583.         else
  1584.             leftMostX = getBufferedBackgroundLeftMostX();
  1585.  
  1586.         if (textLocation == ABOVE_COMPONENT)
  1587.             topMostY = Math.max(0, getTextFinishingPoint().y - textAscent - (highlightText ? extraHighlightHeight/2 : 0));
  1588.         else
  1589.             topMostY = getBufferedBackgroundTopMostY();
  1590.  
  1591.         if (textLocation == BELOW_COMPONENT)
  1592.             bottomMostY = Math.min(getSize().height, getTextFinishingPoint().y + textDescent + (highlightText ? extraHighlightHeight/2 : 0));
  1593.         else
  1594.             bottomMostY = getBufferedBackgroundBottomMostY();
  1595.  
  1596.  
  1597.  
  1598.         if (rightMostX > leftMostX && topMostY < bottomMostY)
  1599.         {
  1600.             return new Rectangle(leftMostX - 1, topMostY - 1,
  1601.                                     rightMostX - leftMostX + 1, bottomMostY - topMostY + 1);
  1602.                                     // +1 and -1 to allow for drawing a rectangle around the text.
  1603.         }
  1604.  
  1605.         //The InfoTipPanel is not large enough for the user to
  1606.         //see any animation, so return null
  1607.         return null;
  1608.     }
  1609.  
  1610.     /**
  1611.      * Utility method for getBufferedBackgroundBounds.
  1612.      *
  1613.      * This method calculates the y-coordinate of the rectangle which bounds the portion
  1614.      * of this component's background which the text will animate over.
  1615.      *
  1616.      * @return this y-coordinate.
  1617.      * @see #getBufferedBackgroundBounds
  1618.      * @see #getBufferedBackgroundBottomMostY
  1619.      * @see #getBufferedBackgroundLeftMostX
  1620.      * @see #getBufferedBackgroundRightMostX
  1621.      */
  1622.     protected int getBufferedBackgroundTopMostY()
  1623.     {
  1624.         int topMostY = 0;
  1625.  
  1626.         switch (textLocation)
  1627.         {
  1628.             case RIGHT_OF_COMPONENT:
  1629.             case LEFT_OF_COMPONENT:
  1630.                 topMostY = getTextConstantYPos() - textAscent - (highlightText ? extraHighlightHeight/2 : 0);
  1631.                 break;
  1632.             case ABOVE_COMPONENT:
  1633.                 topMostY = 0;
  1634.                 break;
  1635.             case BELOW_COMPONENT:
  1636.                 topMostY = (myComponent.getLocation().y + myComponent.getSize().height + PIXEL_BUFFER  - (highlightText ? extraHighlightHeight/2 : 0));
  1637.                 break;
  1638.         }
  1639.  
  1640.         return topMostY;
  1641.     }
  1642.  
  1643.     /**
  1644.      * Utility method for getBufferedBackgroundBounds.
  1645.      *
  1646.      * This method calculates the y-coordinate of the bottom edge of the rectangle
  1647.      * which bounds the portion of this component's background which the text will
  1648.      * animate over.
  1649.      *
  1650.      * @return this y-coordinate.
  1651.      * @see #getBufferedBackgroundBounds
  1652.      * @see #getBufferedBackgroundTopMostY
  1653.      * @see #getBufferedBackgroundLeftMostX
  1654.      * @see #getBufferedBackgroundRightMostX
  1655.      */
  1656.     protected int getBufferedBackgroundBottomMostY()
  1657.     {
  1658.         int bottomMostY = 0;
  1659.  
  1660.         switch (textLocation)
  1661.         {
  1662.             case RIGHT_OF_COMPONENT:
  1663.             case LEFT_OF_COMPONENT:
  1664.                 bottomMostY = getTextConstantYPos() + textDescent + (highlightText ? extraHighlightHeight/2 : 0);
  1665.                 break;
  1666.             case ABOVE_COMPONENT:
  1667.                 bottomMostY = myComponent.getLocation().y - PIXEL_BUFFER + textDescent + (highlightText ? extraHighlightHeight/2 : 0);
  1668.                 break;
  1669.             case BELOW_COMPONENT:
  1670.                 bottomMostY = getSize().height;
  1671.                 break;
  1672.         }
  1673.  
  1674.         return bottomMostY;
  1675.     }
  1676.  
  1677.     /**
  1678.      * Utility method for getBufferedBackgroundBounds.
  1679.      *
  1680.      * This method calculates the x-coordinate of the rectangle which bounds
  1681.      * the portion of this component's background which the text will animate over.
  1682.      *
  1683.      * @return this x-coordinate.
  1684.      * @see #getBufferedBackgroundBounds
  1685.      * @see #getBufferedBackgroundTopMostY
  1686.      * @see #getBufferedBackgroundBottomMostY
  1687.      * @see #getBufferedBackgroundRightMostX
  1688.      */
  1689.     protected int getBufferedBackgroundLeftMostX()
  1690.     {
  1691.         int xPos = 0;
  1692.  
  1693.         switch (textLocation)
  1694.         {
  1695.             case RIGHT_OF_COMPONENT:
  1696.                 xPos = getTextFinishingPoint().x - (highlightText ? extraHighlightWidth/2 : 0);
  1697.                 break;
  1698.             case LEFT_OF_COMPONENT:
  1699.                 xPos = 0;
  1700.                 break;
  1701.             case ABOVE_COMPONENT:
  1702.             case BELOW_COMPONENT:
  1703.                 xPos = getTextConstantXPos() - (highlightText ? extraHighlightWidth/2 : 0);
  1704.                 break;
  1705.         }
  1706.  
  1707.         return xPos;
  1708.     }
  1709.  
  1710.     /**
  1711.      * Utility method for getBufferedBackgroundBounds.
  1712.      *
  1713.      * This method calculates the x-coordinate of the right edge of the rectangle
  1714.      * which bounds the portion of this component's background which the text will
  1715.      * animate over.
  1716.      *
  1717.      * @return this x-coordinate.
  1718.      * @see #getBufferedBackgroundBounds
  1719.      * @see #getBufferedBackgroundTopMostY
  1720.      * @see #getBufferedBackgroundBottomMostY
  1721.      * @see #getBufferedBackgroundLeftMostX
  1722.      */
  1723.     protected int getBufferedBackgroundRightMostX()
  1724.     {
  1725.         int xPos = 0;
  1726.  
  1727.         switch (textLocation)
  1728.         {
  1729.             case RIGHT_OF_COMPONENT:
  1730.                 xPos = getSize().width;
  1731.                 break;
  1732.             case LEFT_OF_COMPONENT:
  1733.                 xPos = myComponent.getLocation().x - PIXEL_BUFFER + (highlightText ? extraHighlightWidth/2 : 0);
  1734.                 break;
  1735.             case ABOVE_COMPONENT:
  1736.             case BELOW_COMPONENT:
  1737.                 xPos = getTextConstantXPos() + textWidth + (highlightText ? extraHighlightWidth/2 : 0);
  1738.                 break;
  1739.         }
  1740.  
  1741.         return xPos;
  1742.     }
  1743.  
  1744.     /**
  1745.      * Calculates the point at which the text should be located when the scrolling
  1746.      * animation is initiated.
  1747.      *
  1748.      * @return this Point
  1749.      * @see #getTextFinishingPoint
  1750.      */
  1751.     protected Point getTextStartingPoint()
  1752.     {
  1753.         Point startingPoint = new Point(0,0);
  1754.  
  1755.         switch (textLocation)
  1756.         {
  1757.             case RIGHT_OF_COMPONENT:
  1758.                 startingPoint.x = getSize().width + (highlightText ? extraHighlightWidth/2 : 0);
  1759.                 startingPoint.y = getTextConstantYPos();
  1760.                 break;
  1761.             case LEFT_OF_COMPONENT:
  1762.                 startingPoint.x = -textWidth - (highlightText ? extraHighlightWidth/2 : 0);
  1763.                 startingPoint.y = getTextConstantYPos();
  1764.                 break;
  1765.             case ABOVE_COMPONENT:
  1766.                 startingPoint.x = getTextConstantXPos();
  1767.                 startingPoint.y = -textDescent -  (highlightText ? extraHighlightHeight/2 : 0);
  1768.                 break;
  1769.             case BELOW_COMPONENT:
  1770.                 startingPoint.x = getTextConstantXPos();
  1771.                 startingPoint.y = getSize().height + textAscent + (highlightText ? extraHighlightHeight/2 : 0);
  1772.                 break;
  1773.         }
  1774.         return startingPoint;
  1775.     }
  1776.  
  1777.     /**
  1778.      * Calculates the point at which the text should be located when the scrolling
  1779.      * animation has scrolled the text all the way in.
  1780.      *
  1781.      * @return this Point
  1782.      * @see #getTextStartingPoint
  1783.      */
  1784.     protected Point getTextFinishingPoint()
  1785.     {
  1786.         Point finishingPoint = new Point(0,0);
  1787.  
  1788.         switch (textLocation)
  1789.         {
  1790.             case RIGHT_OF_COMPONENT:
  1791.                 finishingPoint.x = myComponent.getLocation().x + myComponent.getSize().width + PIXEL_BUFFER;
  1792.                 finishingPoint.y = getTextConstantYPos();
  1793.                 break;
  1794.             case LEFT_OF_COMPONENT:
  1795.                 finishingPoint.x = myComponent.getLocation().x - PIXEL_BUFFER - textWidth;
  1796.                 finishingPoint.y = getTextConstantYPos();
  1797.                 break;
  1798.             case ABOVE_COMPONENT:
  1799.                 finishingPoint.x = getTextConstantXPos();
  1800.                 finishingPoint.y = myComponent.getLocation().y - PIXEL_BUFFER;
  1801.                 break;
  1802.             case BELOW_COMPONENT:
  1803.                 finishingPoint.x = getTextConstantXPos();
  1804.                 finishingPoint.y = myComponent.getLocation().y + myComponent.getSize().height + PIXEL_BUFFER + textAscent;
  1805.                 break;
  1806.         }
  1807.         return finishingPoint;
  1808.     }
  1809.  
  1810.     /**
  1811.      * Adds the specified component to this container at the specified
  1812.      * index.  Also notifies the layout manager to add the component to
  1813.      * the this container's layout using the specified constraints object.
  1814.      * <p>
  1815.      * This is the method to override if you want to track every add
  1816.      * request to a container.  An overriding method should usually
  1817.      * include a call to super.addImpl(comp, constraints, index).
  1818.      *
  1819.      * It is overridden here to set the only
  1820.      * component in this InfoTipPanel. This is the component
  1821.      * that triggers text scrolling on a mouse entered event.
  1822.      * The InfoTipPanel can only contain one component.  Any
  1823.      * previous component will be removed before the new one is added.
  1824.      *
  1825.      * @param comp the component to be added
  1826.      * @param constraints an object expressing layout contraints for this
  1827.      * component
  1828.      * @param index the position in the container's list at which to
  1829.      * insert the component.  -1 means insert at the end.
  1830.      * @see #remove
  1831.      */
  1832.     protected void addImpl(Component comp, Object constraints, int index)
  1833.     {
  1834.         if (callingAddInternally)
  1835.         {
  1836.             //if we are calling add from Within InfoTipPanel do a regular old addImpl()
  1837.             super.addImpl(comp,constraints,index);
  1838.             return;
  1839.         }
  1840.  
  1841.         setComponent(comp);
  1842.     }
  1843.  
  1844.      /**
  1845.      * This method re-creates the off screen images necessary for the text animation.
  1846.      * This method must be called any time that the sizes of these images must be
  1847.      * re-calculated -- it will flush() the old images (if any) and create new images
  1848.      * of the correct size.
  1849.      */
  1850.     protected void createOffScreenBuffers()
  1851.     {
  1852.         if (bufferedBgndImage != null)
  1853.         {
  1854.             bufferedBgndImage.flush();
  1855.             bufferedBgndImage = null;
  1856.         }
  1857.  
  1858.         if (offScreenImage != null)
  1859.         {
  1860.             offScreenImage.flush();
  1861.             offScreenImage = null;
  1862.         }
  1863.  
  1864.         Rectangle bufferedBgndImageBounds = getBufferedBackgroundBounds();
  1865.         if (bufferedBgndImageBounds != null)
  1866.         {
  1867.             bufferedBgndImage = createImage(bufferedBgndImageBounds.width, bufferedBgndImageBounds.height);
  1868.             offScreenImage = createImage(bufferedBgndImageBounds.width, bufferedBgndImageBounds.height);
  1869.         }
  1870.  
  1871.         storeBackground();
  1872.     }
  1873.  
  1874.     /**
  1875.      * Standard method which handles serializing this object.
  1876.      * It has been OVERRIDDEN here to re-calculate all of the
  1877.      * state which is marked transient (including scrollState,
  1878.      * sizes, and offScreenBuffers).
  1879.      */
  1880.     private void readObject(java.io.ObjectInputStream in)
  1881.         throws IOException, ClassNotFoundException
  1882.     {
  1883.         in.defaultReadObject();
  1884.  
  1885.         scrollState = DONE_SCROLLING_OUT;
  1886.  
  1887.         calculateTextAttributes();
  1888.  
  1889.         createOffScreenBuffers();
  1890.     }
  1891.  
  1892.  
  1893.     /**
  1894.      * This internal variable represents the state of the current text-scrolling
  1895.      * animation, if any.
  1896.      * Should be one of: SCROLLING_IN, DONE_SCROLLING_IN, SCROLLING_OUT,
  1897.      * or DONE_SCROLLING_OUT.
  1898.      */
  1899.     transient int scrollState;
  1900.  
  1901.     /**
  1902.      * This property represents the direction from which the text should be
  1903.      * scrolled by the InfoTipPanel.
  1904.      * Should be one of: RIGHT_OF_COMPONENT, LEFT_OF_COMPONENT, ABOVE_COMPONENT,
  1905.      * or BELOW_COMPONENT.
  1906.      * @see #setTextLocation
  1907.      * @see #getTextLocation
  1908.      */
  1909.     int textLocation;
  1910.  
  1911.     /**
  1912.       * The distance between the edge of the added component and the point at which the text stops scrolling.
  1913.       * @see #getTextFinishingPoint
  1914.       */
  1915.     static final int PIXEL_BUFFER = 10;
  1916.  
  1917.     /**
  1918.       * The horizontal distance which the text moves each animation frame (if the text is scrolling left or right).
  1919.       * @see #drawNextFrame
  1920.       */
  1921.     static final int DX_PER_FRAME = 25;
  1922.  
  1923.     /**
  1924.       * The vertical distance which the text moves each animation frame (if the text is scrolling up or down).
  1925.       * @see #drawNextFrame
  1926.       */
  1927.     static final int DY_PER_FRAME = 17;
  1928.  
  1929.     /**
  1930.       * The time (in milliseconds) paused between each animation frame.
  1931.       * @see #scrollOneDirection
  1932.       */
  1933.     static final int TIME_BETWEEN_FRAMES = 50;
  1934.  
  1935.     /**
  1936.      * The thread which will animate the scrolling text.
  1937.      */
  1938.     transient protected Thread textAnimationThread = null;
  1939.     /**
  1940.      * Off screen buffer used for animation.
  1941.      */
  1942.     transient protected Image offScreenImage = null;
  1943.     /**
  1944.      * Off screen buffer used for animation.
  1945.      */
  1946.     transient protected Image bufferedBgndImage = null;
  1947.  
  1948.     /**
  1949.      * Internal flag that indicates a call to add() is being made from within InfoTipPanel.
  1950.      */
  1951.     transient protected boolean    callingAddInternally = false;
  1952.  
  1953.     /**
  1954.      * Internal flag that indicates that the component is NOT to draw the text from within paint().
  1955.      */
  1956.     transient protected boolean    dontDrawText = false;
  1957.  
  1958.     /**
  1959.      * Width of current message text.
  1960.      */
  1961.     transient protected int textWidth;
  1962.  
  1963.     /**
  1964.      * Height of current message text.
  1965.      */
  1966.     transient protected int textHeight;
  1967.  
  1968.     /**
  1969.      * Ascent of current message text.
  1970.      */
  1971.     transient protected int textAscent;
  1972.  
  1973.     /**
  1974.      * Descent of current message text.
  1975.      */
  1976.     transient protected int textDescent;
  1977.  
  1978.     /**
  1979.      * Reference to the component which will be added to the InfoTipPanel and
  1980.      * whose mouse entered and mouse exited events will control the scrolling of the text.
  1981.      * @see #setComponent
  1982.      * @see #getComponent
  1983.      */
  1984.     protected Component myComponent = null;
  1985.  
  1986.     /**
  1987.      * The text which will be scrolled on mouse entered and mouse exited events from myComponent.
  1988.      * @see #setText
  1989.      * @see #getText
  1990.      */
  1991.     protected String text = null;
  1992.  
  1993.     /**
  1994.      * boolean property representing whether the text will be scrolled in from the
  1995.      * edge of the panel on a mouse over, or popped up beside the component.
  1996.      * @see #setTextScrollsIn
  1997.      * @see #getTextScrollsIn
  1998.      */
  1999.     protected boolean textScrollsIn = true;
  2000.  
  2001.     /**
  2002.      * int property which represents how long (in milliseconds) to pause before
  2003.      * starting to scroll/pop-up text on a mouse entered event.
  2004.      * @see #setTextDelay
  2005.      * @see #getTextDelay
  2006.      */
  2007.     protected int textDelay = 0;
  2008.  
  2009.     /**
  2010.      * boolean property representing whether the text will be drawn transparently
  2011.      * rather than using the specified textHighlightColor.
  2012.      * @see #setHighlightText
  2013.      * @see #getHighlightText
  2014.      */
  2015.     protected boolean highlightText = false;
  2016.  
  2017.     /**
  2018.      * The color used for text when this component is enabled.
  2019.      * @see #getTextColor
  2020.      * @see #setTextColor
  2021.      */
  2022.     protected Color textColor = null;
  2023.  
  2024.     /**
  2025.      * Color which will be used to highlight the text if highlightText flag is set.
  2026.      * @see #setTextHighlightColor
  2027.      * @see #getTextHighlightColor
  2028.      */
  2029.     protected Color textHighlightColor = null;
  2030.  
  2031.     /**
  2032.      * Timer object which is used to time the delay before popping up the text.
  2033.      */
  2034.     protected Timer delayTimer = null;
  2035.  
  2036.     /**
  2037.      * Reference to the Mouse object which is listening to myComponent for mouse events.
  2038.      */
  2039.     protected Mouse myMouseListener = null;
  2040.  
  2041.     /**
  2042.      * Extra width which is added on if highlighText == true
  2043.      */
  2044.     protected int extraHighlightWidth;
  2045.  
  2046.     /**
  2047.      * Extra height which is added on if highlighText == true
  2048.      */
  2049.     protected int extraHighlightHeight;
  2050.  
  2051.     /**
  2052.      * Defines the "scrolling in" scroll state.  One of four possible values for scrollState.
  2053.      */
  2054.     protected static final int SCROLLING_IN = 0;
  2055.     /**
  2056.      * Defines the "done scrolling in" scroll state.  One of four possible values for scrollState.
  2057.      */
  2058.     protected static final int DONE_SCROLLING_IN = 1;
  2059.     /**
  2060.      * Defines the "scrolling out" scroll state.  One of four possible values for scrollState.
  2061.      */
  2062.     protected static final int SCROLLING_OUT = 2;
  2063.     /**
  2064.      * Defines the "done scrolling out" scroll state.  One of four possible values for scrollState.
  2065.      */
  2066.     protected static final int DONE_SCROLLING_OUT = 3;
  2067.     /**
  2068.      * Defines the "waiting to scroll in" scroll state.  One of four possible values for scrollState.
  2069.      */
  2070.     protected static final int WAITING_TO_SCROLL_IN = 4;
  2071.  
  2072.     transient protected ResourceBundle errors;
  2073.  
  2074.     // Private members
  2075.     private LocationVeto locationVeto = null;
  2076.  
  2077.     private symantec.itools.beans.VetoableChangeSupport vetos = new symantec.itools.beans.VetoableChangeSupport(this);
  2078.     private symantec.itools.beans.PropertyChangeSupport changes = new symantec.itools.beans.PropertyChangeSupport(this);
  2079.     //{{DECLARE_CONTROLS
  2080.     //}}
  2081. }