home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programming Languages Suite
/
ProgLangD.iso
/
VCAFE.3.0A
/
Main.bin
/
InfoTipPanel.java
< prev
next >
Wrap
Text File
|
1998-09-23
|
63KB
|
2,068 lines
package symantec.itools.awt;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.ColorModel;
import java.beans.PropertyVetoException;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.VetoableChangeListener;
import java.io.IOException;
import java.net.URL;
import symantec.itools.awt.ImagePanel;
import symantec.itools.util.GeneralUtils;
import symantec.itools.util.Timer;
import java.util.ResourceBundle;
// 09/08/97 LAB Conformed to Good Bean Spec.
// 09/10/97 BJC Implementation of Serializable and bug fixes.
/**
* The InfoTipPanel is a panel which may contain only
* one component.
* Whenever this component receives a mouse entered event, the
* InfoTipPanel either scrolls a text message towards the button
* or pops the text up immediately next to the button.
* Whenever this component receives a mouse exit event, the
* text scrolls away from the component or disappears.
*
* @version 1.0, September 4, 1997
* @author Symantec
*/
public class InfoTipPanel extends ImagePanel implements Runnable, java.io.Serializable
{
/**
* Defines the "from right" scroll direction. One of four possible values for textLocation.
*/
public static final int RIGHT_OF_COMPONENT = 0;
/**
* Defines the "from left" scroll direction. One of four possible values for textLocation.
*/
public static final int LEFT_OF_COMPONENT = 1;
/**
* Defines the "from top" scroll direction. One of four possible values for textLocation.
*/
public static final int ABOVE_COMPONENT = 2;
/**
* Defines the "from bottom" scroll direction. One of four possible values for textLocation.
*/
public static final int BELOW_COMPONENT = 3;
/**
* Constructs a default InfoTipPanel.
* The panel is initialized with a null component, RIGHT_OF_COMPONENT as the scrolldirection,
* and the Auto-Position property set to true.
*/
public InfoTipPanel()
{
this(null, "", RIGHT_OF_COMPONENT, true, 0, false);
}
/**
* Constructs a new InfoTipPanel initialized with the
* specified component, text, minimum height and
* minimum width.
* @param component the component which will be added to the InfoTipPanel and
* whose mouse entered and mouse exited events will control the scrolling of the text.
* @param text the text which will be scrolled.
* @param textLocation the direction from which the text is scrolled.
* resize itself and position the component placed inside of it.
*/
public InfoTipPanel(Component component, String text, int textLocation, boolean textScrollsIn, int textDelay, boolean highlightText)
{
scrollState = DONE_SCROLLING_OUT; //the text starts fully extended, waiting to start scrolling in on a mouse entered event
myMouseListener = new Mouse();
delayTimer = new Timer();
if (delayTimer != null)
delayTimer.addActionListener(new Action());
super.setLayout(null);
try
{
setHighlightText(highlightText);
setTextHighlightColor(new Color(16776960)); //default to yellow
setTextColor(new Color(0)); //default to black
setTextScrollsIn(textScrollsIn);
setTextDelay(textDelay);
setText(text);
setTextLocation(textLocation);
}
catch(PropertyVetoException e) {}
setComponent(component);
}
/**
* Set the amount of time (in milliseconds) to wait after a mouse
* entered event before the text is scrolled in or popped up.
*
* @param newDelay the amount of time (in milliseconds) to wait after a mouse
* entered event before the text is scrolled in or popped up.
* @see #getTextDelay
* @exception PropertyVetoException
* if the specified property value is unacceptable
*/
public void setTextDelay(int newDelay) throws PropertyVetoException
{
if (textDelay != newDelay)
{
Integer oldTextDelay = new Integer(textDelay);
Integer newTextDelay = new Integer(newDelay);
vetos.fireVetoableChange("textDelay", oldTextDelay, newTextDelay);
textDelay = newDelay;
delayTimer.setDelay(textDelay); //let the timer know that we will want a new delay time
changes.firePropertyChange("textDelay", oldTextDelay, newTextDelay);
}
}
/**
* Get the amount of time (in milliseconds) to wait after a mouse
* entered event before the text is scrolled in or popped up.
*
* @return the amount of time (in milliseconds) that this InfoTipPanel
* will wait after a mouse entered event before the text is scrolled in
* or popped up.
* @see #setTextDelay
*/
public int getTextDelay()
{
return textDelay;
}
/**
* Sets the "Text Scrolls In" property which tells the InfoTipPanel whether
* the text should be scrolled in on a mouse over rather than just appearing
* next to the component (without scrolling in).
*
* @param newScroll the new value for this property
* @see #getTextScrollsIn
* @exception PropertyVetoException
* if the specified property value is unacceptable
*/
public void setTextScrollsIn(boolean newScroll) throws PropertyVetoException
{
if (textScrollsIn != newScroll)
{
Boolean oldTextScrollsIn = new Boolean(textScrollsIn);
Boolean newTextScrollsIn = new Boolean(newScroll);
vetos.fireVetoableChange("textScrollsIn", oldTextScrollsIn, newTextScrollsIn);
stopScrolling();
textScrollsIn = newScroll;
changes.firePropertyChange("textScrollsIn", oldTextScrollsIn, newTextScrollsIn);
}
}
/**
* Gets the value of the "Text Scrolls In" property which tells the
* InfoTipPanel whether the text should be scrolled in on a mouse over
* rather than just appearing next to the component (without scrolling in).
*
* @return the current value of this property
* @see #setTextScrollsIn
*/
public boolean getTextScrollsIn()
{
return textScrollsIn;
}
/**
* Sets where the text will be located in this InfoTipPanel. This controls
* where the text will appear on a mouse entered event.
* Causes the component to redraw.
*
* @param newLocation the new text location: RIGHT_OF_COMPONENT,
* LEFT_OF_COMPONENT, ABOVE_COMPONENT, or BELOW_COMPONENT
* @see #getTextLocation
* @exception PropertyVetoException
* if the specified property value is unacceptable
*/
public void setTextLocation(int newLocation) throws PropertyVetoException
{
if (textLocation != newLocation)
{
Integer oldTextLocation = new Integer(textLocation);
Integer newTextLocation = new Integer(newLocation);
vetos.fireVetoableChange("textLocation", oldTextLocation, newTextLocation);
stopScrolling();
textLocation = newLocation;
createOffScreenBuffers();
repaint();
changes.firePropertyChange("textLocation", oldTextLocation, newTextLocation);
}
}
/**
* Gets the current value of the "Text Location" property for this InfoTipPanel.
* The text location controls where the text will appear on a mouse entered event.
*
* @return the text location: RIGHT_OF_COMPONENT, LEFT_OF_COMPONENT,
* ABOVE_COMPONENT, or BELOW_COMPONENT
* @see #setTextLocation
*/
public int getTextLocation()
{
return textLocation;
}
/**
* Sets the text in this InfoTipPanel. This is the text
* which scrolls out on a mouse entered event.
* Causes the component to redraw.
*
* @param newText the text
* @exception PropertyVetoException
* if the specified property value is unacceptable
* @see #getText
*/
public void setText(String newText) throws PropertyVetoException
{
if (!symantec.itools.util.GeneralUtils.objectsEqual(text, newText))
{
String oldText = text;
vetos.fireVetoableChange("text", oldText, newText);
stopScrolling();
text = newText;
calculateTextAttributes();
changes.firePropertyChange("text", oldText, newText);
}
}
/**
* Gets the current text in this InfoTipPanel. This is the text
* which scrolls out on a mouse entered event.
*
* @return the text
* @see #setText
*/
public String getText()
{
return new String(text);
}
/**
* Sets the text color.
* Causes the InfoTipPanel to redraw.
* @param color the new color for the text
* @see #getTextColor
* @exception PropertyVetoException
* if the specified property value is unacceptable
*/
public void setTextColor(Color color) throws PropertyVetoException
{
if(! symantec.itools.util.GeneralUtils.objectsEqual(textColor, color))
{
Color oldValue = textColor;
vetos.fireVetoableChange("textColor", oldValue, color);
if (textAnimationThread != null)
textAnimationThread.suspend();
textColor = color;
repaint();
if (textAnimationThread != null)
textAnimationThread.resume();
changes.firePropertyChange("textColor", oldValue, color);
}
}
/**
* Gets the current text color.
* @return the current color of the text
* @see #setTextColor
*/
public Color getTextColor()
{
return textColor;
}
/**
* Sets the current highlight color which will be used if highlightText
* is true.
*
* @param color the new highlight color
* @exception PropertyVetoException
* if the specified property value is unacceptable
* @see #getTextHighlightColor
* @see #getHighlightText
* @see #setHighlightText
*/
public void setTextHighlightColor(Color color) throws PropertyVetoException
{
if (!GeneralUtils.objectsEqual(textHighlightColor, color))
{
Color oldValue = textHighlightColor;
vetos.fireVetoableChange("textHighlightColor", oldValue, color);
if (textAnimationThread != null)
textAnimationThread.suspend();
textHighlightColor = color;
if (highlightText)
repaint();
if (textAnimationThread != null)
textAnimationThread.resume();
changes.firePropertyChange("textHighlightColor", oldValue, color);
}
}
/**
* Gets the current highlight color which will be used if highlightText
* is true.
*
* @return the current highlight color
* @see #setTextHighlightColor
* @see #getHighlightText
* @see #setHighlightText
*/
public Color getTextHighlightColor()
{
return textHighlightColor;
}
/**
* Sets the "Highlight Text" property which determines whether the text
* will be highlighted with the current textHighlightColor or not.
*
* @param newValue the new value of this property
* @exception PropertyVetoException
* if the specified property value is unacceptable
* @see #getHighlightText
* @see #getTextHighlightColor
* @see #setTextHighlightColor
*/
public void setHighlightText(boolean newValue) throws PropertyVetoException
{
if (highlightText != newValue)
{
stopScrolling();
Boolean oldHighlightText = new Boolean(highlightText);
Boolean newHighlightText = new Boolean(newValue);
vetos.fireVetoableChange("highlightText", oldHighlightText, newHighlightText);
highlightText = newValue;
extraHighlightWidth = (highlightText ? 6 : 0);
extraHighlightHeight = (highlightText ? 4 : 0);
changes.firePropertyChange("highlightText", oldHighlightText, newHighlightText);
}
}
/**
* Gets the current value of the "Highlight Text" property which
* determines whether the text will be highlighted with the current
* textHighlightColor or not.
*
* @return the current value of this property
* @see #setHighlightText
* @see #getTextHighlightColor
* @see #setTextHighlightColor
*/
public boolean getHighlightText()
{
return highlightText;
}
/**
* Reshapes the Component to the specified bounding box.
*
* It has been OVERRIDDEN here to adjust all the animation buffers
* to the correct size (based on the new size of the InfoTipPanel)
*
* @param x the x coordinate
* @param y the y coordinate
* @param width the width of the component
* @param height the height of the component
* @see java.awt.Component#getBounds
* @see java.awt.Component#setLocation
* @see java.awt.Component#setSize
*/
public void setBounds(int x, int y, int width, int height)
{
stopScrolling();
super.setBounds(x, y, width, height);
createOffScreenBuffers();
}
/**
* Sets the URL of the image to display in this panel.
*
* It has been OVERRIDDEN here to suspend the animation thread while the image
* is set, and to re-buffer the new background.
*
* @param url the URL of the image to display
* @see symantec.itools.awt.ImagePanel#setImageURL
* @see symantec.itools.awt.ImagePanel#getImageURL
* @exception PropertyVetoException
* if the specified property value is unacceptable
*/
public void setImageURL(URL url) throws PropertyVetoException
{
if (textAnimationThread != null)
textAnimationThread.suspend();
super.setImageURL(url);
storeBackground();
if (textAnimationThread != null)
textAnimationThread.resume();
}
/**
* Sets the new panel image style.
*
* It has been OVERRIDDEN here to suspend the animation thread while the style
* is set, and to re-buffer the new background.
*
* @param newStyle the new panel image style, one of
* IMAGE_TILED, IMAGE_CENTERED, or IMAGE_SCALED_TO_FIT
* @exception PropertyVetoException
* if the specified property value is unacceptable
* @see symantec.itools.awt.ImagePanel#setStyle
* @see symantec.itools.awt.ImagePanel#getStyle
* @see symantec.itools.awt.ImagePanel#IMAGE_TILED
* @see symantec.itools.awt.ImagePanel#IMAGE_CENTERED
* @see symantec.itools.awt.ImagePanel#IMAGE_SCALED_TO_FIT
*/
public void setStyle(int newStyle) throws PropertyVetoException
{
if (textAnimationThread != null)
textAnimationThread.suspend();
super.setStyle(newStyle);
storeBackground();
if (textAnimationThread != null)
textAnimationThread.resume();
}
/**
* Sets the component in this InfoTipPanel. This is the component
* that triggers text scrolling on a mouse entered event. The InfoTipPanel
* can only contain one component. Any previous component will be
* removed before the new one is added.
*
* @param comp the component to add
* @see #getComponent
*/
public void setComponent(Component comp)
{
removeAll(); //we rely on the fact that removeAll() calls stopScrolling()
myComponent = comp;
//Add the new component at the end
callingAddInternally = true;
try
{
if (comp != null)
super.add(myComponent);
}
finally
{
callingAddInternally = false;
}
createOffScreenBuffers();
if (myComponent != null)
myComponent.addMouseListener(myMouseListener);
}
/**
* Gets the current component in the InfoTipPanel.
* This is the component that triggers text scrolling on a
* mouse entered event.
* @return the current component in the InfoTipPanel
* @see #setComponent
*/
public Component getComponent()
{
return myComponent;
}
/**
* Sets the font of the component.
* This is a standard method of java.awt.component.
*
* It has been OVERRIDDEN here to recalculate the sizes/locations
* of everything based on the new font.
*
* @param f the font
*/
public synchronized void setFont(Font f)
{
stopScrolling();
super.setFont(f);
calculateTextAttributes();
createOffScreenBuffers();
}
/**
* Returns the recommended dimensions to properly display this component.
* This is a standard Java AWT method which gets called to determine
* the recommended size of this component.
*
* It has been OVERRIDEEN here to, for each axis, return the larger of
* the current size and the size of myComponent.
*
* @return A Dimension representing the preferred size of this component
* @see #getMinimumSize
*/
public Dimension getPreferredSize()
{
int preferredWidth = Math.max(getSize().width, myComponent == null ? 0 : myComponent.getSize().width);
int preferredHeight = Math.max(getSize().height, myComponent == null ? 0 : myComponent.getSize().height);
return new Dimension(preferredWidth, preferredHeight);
}
/**
* Returns the minimum dimensions to properly display this component.
* This is a standard Java AWT method which gets called to determine
* the minimum size of this component.
*
* It is OVERRIDDEN here to just return the preferred size as determined
* by a call to getPreferredSize()
*
* @return A Dimension representing the minimum size of this component
* @see #getPreferredSize
*/
public Dimension getMinimumSize()
{
return getPreferredSize();
}
/**
* Takes no action.
* This is a standard Java AWT method which gets called to specify
* which layout manager should be used to layout the components in
* standard containers.
*
* Since layout managers CANNOT BE USED with this container the standard
* setLayout has been OVERRIDDEN for this container and does nothing.
*
* @param l the layout manager to use to layout this container's components
* (IGNORED)
* @see java.awt.Container#getLayout
**/
public void setLayout(LayoutManager mgr)
{
}
/**
* Paints this component using the given graphics context.
* This is a standard Java AWT method which typically gets called
* by the AWT to handle painting this component. It paints this component
* using the given graphics context. The graphics context clipping region
* is set to the bounding rectangle of this component and its [0,0]
* coordinate is this component's top-left corner.
*
* It has been OVERRIDDEN for this container to call super.paint(g), and
* then draw it's text, if necessary.
*
* @param g the graphics context used for painting
* @see java.awt.Component#repaint
*/
public void paint(Graphics g)
{
super.paint(g);
if (!dontDrawText)
{
if (scrollState == DONE_SCROLLING_IN)
{
//If the text is supposed to be scrolled in all the way, we must draw it.
//Note that if the text is in the process of scrolling in or out the animation
//thread will handle drawing it at the appropriate time.
Point textPosition = getTextFinishingPoint();
drawText(g, textPosition.x, textPosition.y);
}
}
}
/**
* Tells this component that it has been added to a container.
* This is a standard Java AWT method which gets called by the AWT when
* this component is added to a container. Typically, it is used to
* create this component's peer.
*
* It has been OVERRIDDEN here to calculate text sizes and to create
* any off screen buffers that will be needed for the animation.
*
* @see #removeNotify
*/
public synchronized void addNotify()
{
super.addNotify();
errors = ResourceBundle.getBundle("symantec.itools.resources.ErrorsBundle");
//re-calculate sizes and re-create off screen images now that they have a graphics context
calculateTextAttributes();
createOffScreenBuffers();
locationVeto = new LocationVeto();
addTextLocationListener(locationVeto);
}
/**
* Tells this component that it is being removed from a container.
* This is a standard Java AWT method which gets called by the AWT when
* this component is removed from a container. Typically, it is used to
* destroy the peers of this component and all its subcomponents.
*
* It has been OVERRIDDEN here to dispose of any buffers or any
* animation thread in use by this InfoTipPanel, and then
* call super.removeNotify().
*
* @see #addNotify
*/
public synchronized void removeNotify()
{
stopScrolling();
if (offScreenImage != null)
{
offScreenImage.flush();
offScreenImage = null;
}
if (bufferedBgndImage != null)
{
bufferedBgndImage.flush();
bufferedBgndImage = null;
}
removeTextLocationListener(locationVeto);
locationVeto = null;
super.removeNotify();
}
/**
* Standard method in the java.lang.Runnable interface.
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
*
* Here, run has been implemented to do animate the text scrolling in and out.
* The thread calling this method will continue to animate the text until it
* scrolls either completely out, or completely in.
*
* @see java.lang.Thread#run
* @since JDK1.0
*/
public void run()
{
if (text != null && validateOffScreenBuffers())
{
switch (scrollState) {
case SCROLLING_OUT:
if (textScrollsIn)
scrollText();
else
popDownText();
break;
case SCROLLING_IN:
if (textScrollsIn)
scrollText();
else
popUpText();
break;
}
}
textAnimationThread = null;
}
/**
* Removes the specified component from this container.
* This is a standard Java AWT method which gets called to remove a
* component from a container. When this happens the component's
* removeNotify() will also get called to indicate component removal.
*
* It has been OVERRIDDEN here to add remove the listeners which we added
* to the component.
*
* @param comp the component to remove
* @see #removeAll
* @see java.awt.Container#add
*/
public synchronized void remove(Component comp)
{
stopScrolling();
super.remove(comp);
if (comp == myComponent)
{
myComponent.removeMouseListener(myMouseListener);
myComponent = null;
}
}
/**
* Removes all the components from this container.
* This is a standard Java AWT method which gets called to remove all
* the components from a container. When this happens each component's
* removeNotify() will also get called to indicate component removal.
*
* It has been OVERRIDDEN here to remove the InfoTipPanel's
* reference to its component.
*
* @see #remove
* @see java.awt.Container#add
*/
public synchronized void removeAll()
{
stopScrolling();
if (this.myComponent != null)
super.remove(this.myComponent);
myComponent = null;
}
/**
* Adds a listener for all event changes.
* @param listener the listener to add.
* @see #removePropertyChangeListener
*/
public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
{
super.addPropertyChangeListener(listener);
changes.addPropertyChangeListener(listener);
}
/**
* Removes a listener for all event changes.
* @param listener the listener to remove.
* @see #addPropertyChangeListener
*/
public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
{
super.removePropertyChangeListener(listener);
changes.removePropertyChangeListener(listener);
}
/**
* Adds a vetoable listener for all event changes.
* @param listener the listener to add.
* @see #removeVetoableChangeListener
*/
public synchronized void addVetoableChangeListener(VetoableChangeListener listener)
{
super.addVetoableChangeListener(listener);
vetos.addVetoableChangeListener(listener);
}
/**
* Removes a vetoable listener for all event changes.
* @param listener the listener to remove.
* @see #addVetoableChangeListener
*/
public synchronized void removeVetoableChangeListener(VetoableChangeListener listener)
{
super.removeVetoableChangeListener(listener);
vetos.removeVetoableChangeListener(listener);
}
/**
* Adds a listener for the TextLocation property changes.
* @param listener the listener to add.
* @see #removeTextLocationListener(java.beans.PropertyChangeListener)
*/
public synchronized void addTextLocationListener(PropertyChangeListener listener)
{
changes.addPropertyChangeListener("textLocation", listener);
}
/**
* Removes a listener for the TextLocation property changes.
* @param listener the listener to remove.
* @see #addTextLocationListener(java.beans.PropertyChangeListener)
*/
public synchronized void removeTextLocationListener(PropertyChangeListener listener)
{
changes.removePropertyChangeListener("textLocation", listener);
}
/**
* Adds a vetoable listener for the TextLocation property changes.
* @param listener the listener to add.
* @see #removeTextLocationListener(java.beans.VetoableChangeListener)
*/
public synchronized void addTextLocationListener(VetoableChangeListener listener)
{
vetos.addVetoableChangeListener("textLocation", listener);
}
/**
* Removes a vetoable listener for the TextLocation property changes.
* @param listener the listener to remove.
* @see #addTextLocationListener(java.beans.VetoableChangeListener)
*/
public synchronized void removeTextLocationListener(VetoableChangeListener listener)
{
vetos.removeVetoableChangeListener("textLocation", listener);
}
/**
* Implementation of the MouseListener interface so that we can trigger
* text scrolling on mouseEntered() and mouseExited().
*/
class Mouse extends MouseAdapter
{
public void mouseEntered(MouseEvent e)
{
startScrolling(SCROLLING_IN, false);
}
public void mouseExited(MouseEvent e)
{
startScrolling(SCROLLING_OUT, false);
}
}
/**
* Implementation of the ActionListener interface so that we can trigger
* text scrolling after a delay.
*/
class Action implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
startScrolling(SCROLLING_IN, true); //true so that we don't start another timer and wait again
}
}
/**
* This is the PropertyChangeEvent handling inner class for the constrained TextLocation property.
* Handles vetoing TextLocations that are outside of the valid range.
*/
class LocationVeto implements java.beans.VetoableChangeListener, java.io.Serializable
{
/**
* This method gets called when an attempt to change the constrained TextLocation property is made.
* Ensures the given text location is valid.
*
* @param e a <code>PropertyChangeEvent</code> object describing the
* event source and the property that has changed.
* @exception PropertyVetoException if the recipient wishes the property
* change to be rolled back.
*/
public void vetoableChange(PropertyChangeEvent e) throws PropertyVetoException
{
int i = ((Integer)e.getNewValue()).intValue();
if (!isValidTextLocation(i))
{
throw new PropertyVetoException(errors.getString("InvalidTextLocation") + i, e);
}
}
}
/**
* Is the given text location valid.
* @param i the given text location
* @return true if the given text location is acceptable, false if not.
*/
protected boolean isValidTextLocation(int i)
{
switch(i)
{
case RIGHT_OF_COMPONENT:
case LEFT_OF_COMPONENT:
case ABOVE_COMPONENT:
case BELOW_COMPONENT:
return true;
default:
return false;
}
}
/**
* Draws the current text into the graphics context and location specified
* in the parameters. Highlights the text, if necessary.
*
* @param g the graphics context into which the text should be drawn
* @param x the x-coordinate at which the text should be drawn
* @param y the y-coordinate at which the text should be drawn
*/
protected void drawText(Graphics g, int x, int y)
{
Color savedColor = g.getColor();
if (highlightText)
{
g.setColor(textHighlightColor);
Rectangle highlightBounds = getHighlightBounds(x, y);
if (highlightBounds != null)
g.fillRect(highlightBounds.x, highlightBounds.y, highlightBounds.width, highlightBounds.height);
g.setColor(textColor);
g.drawRect(highlightBounds.x,
highlightBounds.y,
highlightBounds.width - 1,
highlightBounds.height - 1);
// -1 so that we fit the rectangle within the bounds
}
g.setColor(textColor);
g.drawString(text, x, y);
g.setColor(savedColor);
}
/**
* Determines the rectangle which bounds the area that should be highlighted.
*
* @return this bounding Rectangle, or null if textWidth or textHeight is zero.
*/
protected Rectangle getHighlightBounds(int textX, int textY)
{
if (textWidth > 0 && textHeight > 0)
return new Rectangle(textX - extraHighlightWidth/2,
textY - textAscent - extraHighlightHeight/2,
textWidth + extraHighlightWidth,
textHeight + extraHighlightHeight);
else
return null;
}
/**
* Starts the text scrolling in the direction indicated by the parameter
* newScrollState. The text will continue to be scrolled back and
* forth until the text scrolls all the way in, or all the way out.
* Causes a thread to be started to animate the text.
*
* This method is also responsible for starting the timer if the textDelay
* is set to some number of milliseconds. This method will be called a
* second time if and when that timer goes off.
*
* @param newScrollState the direction of the scroll: SCROLLING_IN,
* or SCROLLING_OUT
* @param ignoreDelay whether or not to ignore a delay even if it would normally
* be necessary (i.e. even though the text is starting to scroll in)
* @see #stopScrolling
*/
protected void startScrolling(int newScrollState, boolean ignoreDelay)
{
delayTimer.stop();
if ((scrollState == DONE_SCROLLING_OUT && newScrollState == SCROLLING_IN)
|| (scrollState == WAITING_TO_SCROLL_IN && newScrollState == SCROLLING_IN))
{
//Start scrolling in!
//Start the animation or the timer, whichever is appropriate
if (ignoreDelay || textDelay == 0)
{
scrollState = newScrollState;
textAnimationThread = new Thread(this);
textAnimationThread.start();
}
else
{
scrollState = WAITING_TO_SCROLL_IN;
delayTimer.start();
}
}
else if (scrollState == DONE_SCROLLING_IN && newScrollState == SCROLLING_OUT)
{
//Start scrolling out!
scrollState = newScrollState;
textAnimationThread = new Thread(this);
textAnimationThread.start();
}
else if (scrollState == WAITING_TO_SCROLL_IN && newScrollState == SCROLLING_OUT)
{
//We never actually started the scrolling in animation before this mouseExited event
//occurred, so all we have to do is set the scrollState to DONE_SCROLLING_OUT
scrollState = DONE_SCROLLING_OUT;
}
else
{
//We should already be in the process of scrolling text one way or the other. Just
//update scrollState, and the animation thread will take care of the rest.
scrollState = newScrollState;
}
}
/**
* Stops the scrolling text animation (if any). Any subsequent mouse
* over events will cause the text to scroll again, as usual.
*
* @see #startScrolling
*/
protected void stopScrolling()
{
delayTimer.stop(); //these two lines stop any pending scrolls
if (scrollState != DONE_SCROLLING_IN)
scrollState = DONE_SCROLLING_OUT; //It's OK if the text is already done scrolling in. Otherwise,
//set the state to DONE_SCROLLING_OUT
if (textAnimationThread != null)
{
textAnimationThread.stop();
textAnimationThread = null;
repaint(); //repaint to get rid of text which may be partially scrolled in
}
}
/**
* Draws the text next to the component (in the location specified
* by textLocation).
*
* @see #popDownText
*/
protected void popUpText()
{
//with this one call we draw the text, and also update the scrollState.
drawNextFrame(getTextFinishingPoint());
}
/**
* Basically just erases the text next to the component (in the location specified
* by textLocation).
*
* @see #popUpText
*/
protected void popDownText()
{
Graphics g = getGraphics();
if (g != null)
{
Rectangle bufferedBgndImageBounds = getBufferedBackgroundBounds();
if (bufferedBgndImageBounds != null)
{
//just paint our buffered background
g.drawImage(bufferedBgndImage, bufferedBgndImageBounds.x, bufferedBgndImageBounds.y, this);
//all done! Set our scrollState accordingly.
scrollState = DONE_SCROLLING_OUT;
}
}
}
/**
* Start the text scrolling in or out from the appropriate location.
* The text will continue to be scrolled back and forth until the text
* scrolls all the way in, or all the way out.
*/
protected void scrollText()
{
Point currentTextLocation;
if (scrollState == SCROLLING_IN)
currentTextLocation = getTextStartingPoint();
else
currentTextLocation = getTextFinishingPoint();
while (scrollState != DONE_SCROLLING_IN && scrollState != DONE_SCROLLING_OUT)
currentTextLocation = scrollOneDirection(currentTextLocation);
}
/**
* Utility method of run
*
* This method checks to see whether the off screen images necessary for
* the animation already exist, and if so that they are of the correct size.
* If the buffers do not exist this method tries to create them.
*
* Due to a bug in some VMs componentResize events may not get sent correctly
* so we must also check that the sizes of the buffers are correct.
*
* @return a boolean indicating whether or not buffers exist by the end of
* this method which are sufficient for the animation.
* @see #run
*/
protected boolean validateOffScreenBuffers()
{
if (offScreenImage == null || bufferedBgndImage == null)
{
createOffScreenBuffers();
}
else
{
//Since component resize events are not sent correctly on some VMs,
//we must check to see if the component size is in sync with the
//off screen buffer sizes. Once this works in all VMs we can accomplish
//this through listening to component resize events.
Rectangle correctBounds = getBufferedBackgroundBounds();
if (correctBounds != null)
{
if (offScreenImage.getWidth(this) != correctBounds.width
|| offScreenImage.getHeight(this) != correctBounds.height)
{
createOffScreenBuffers();
}
}
}
return (offScreenImage != null && bufferedBgndImage != null);
}
/**
* Utility method for run.
*
* This method stores the background (in this case only the panel's image
* since this is a heavyweight component) into bufferedBgndImage.
* Only the portion of the background inside of the bounds specified by
* getBufferedBackgroundBounds() is saved.
*
* @see #run
*/
protected void storeBackground()
{
if (bufferedBgndImage != null)
{
Graphics buffer = bufferedBgndImage.getGraphics();
Rectangle bufferBounds = getBufferedBackgroundBounds();
if (buffer != null && bufferBounds != null)
{
buffer.translate(-bufferBounds.x, -bufferBounds.y);
dontDrawText = true;
try
{
paint(buffer);
}
finally
{
dontDrawText = false;
}
buffer.translate(bufferBounds.x, bufferBounds.y);
}
}
}
/**
* Starts the text scrolling in the current direction, starting from
* the point specified by the parameter startingPoint.
* The text will continue to be scrolled until the current direction
* changes (due to a mouse event, or due to the text scrolling all
* the way in or out).
*
* @param startingPoint the point from which the text starts scrolling
* @return the point at which the text stopped scrolling
*/
protected Point scrollOneDirection(Point startingPoint)
{
Point currentPoint = startingPoint;
int currentDirection = scrollState;
while(scrollState == currentDirection)
{
try
{
textAnimationThread.sleep(TIME_BETWEEN_FRAMES);
}
catch (InterruptedException e) {}
drawNextFrame(currentPoint);
}
return currentPoint;
}
/**
* Does all necessary calculations and graphics work to advance the text one frame
* (starting from the point specified in the paramter startingPoint).
*
* statingPoint is modified by this method to be the NEW position of the text (scrolled
* one frame further than when the method was called)
*
* @param startingPoint the current position of the text (before this method advances it
* by one animation frame).
*/
protected void drawNextFrame(Point startingPoint)
{
Graphics g = getGraphics();
if (g != null)
{
Graphics offScreenGraphics = offScreenImage.getGraphics();
Rectangle bufferedBgndImageBounds = getBufferedBackgroundBounds();
if (bufferedBgndImageBounds != null)
{
//first, paint our buffered background
offScreenGraphics.drawImage(bufferedBgndImage, 0, 0, this);
//now, paint our text on top of that
startingPoint.x += calculateDX(startingPoint);
startingPoint.y += calculateDY(startingPoint);
drawText(offScreenGraphics, startingPoint.x - bufferedBgndImageBounds.x, startingPoint.y - bufferedBgndImageBounds.y);
//take the resulting off screen image and paint it into our 'on-screen' graphics context
g.drawImage(offScreenImage, bufferedBgndImageBounds.x, bufferedBgndImageBounds.y, this);
//determine what direction we should be scrolling in now
updateDirection(startingPoint);
}
}
}
/**
* Utility method for drawNextFrame.
*
* Given the current location of the text, this method calculates how far in the
* x-direction the text should move when it advances to the next frame of the animation.
*
* @param startingPoint the current location of the text.
* @return the distance in the x-direction the text should move when it advances
* to the next frame of the animation.
* @see #calculateDY
*/
protected int calculateDX(Point startingPoint)
{
int dx = 0;
Point destination = new Point();
if (textLocation == ABOVE_COMPONENT || textLocation == BELOW_COMPONENT)
return 0;
switch (scrollState)
{
case SCROLLING_IN:
destination = getTextFinishingPoint();
if (atOrBeyondPoint(startingPoint, destination, DX_PER_FRAME))
dx = -(startingPoint.x - destination.x);
else
dx = (textLocation == LEFT_OF_COMPONENT ? DX_PER_FRAME : -DX_PER_FRAME);
break;
case SCROLLING_OUT:
destination = getTextStartingPoint();
if (atOrBeyondPoint(startingPoint, destination, DX_PER_FRAME))
dx = (destination.x - startingPoint.x);
else
dx = (textLocation == LEFT_OF_COMPONENT ? -DX_PER_FRAME : DX_PER_FRAME);
break;
case DONE_SCROLLING_OUT:
case DONE_SCROLLING_IN:
dx = 0;
break;
}
return dx;
}
/**
* Utility method for drawNextFrame.
*
* Given the current location of the text, this method calculates how far in the
* y-direction the text should move when it advances to the next frame of the animation.
*
* @param startingPoint the current location of the text.
* @return the distance in the y-direction the text should move when it advances
* to the next frame of the animation.
* @see #calculateDX
*/
protected int calculateDY(Point startingPoint)
{
int dy = 0;
Point destination;
if (textLocation == RIGHT_OF_COMPONENT || textLocation == LEFT_OF_COMPONENT)
return 0;
switch (scrollState)
{
case SCROLLING_IN:
destination = getTextFinishingPoint();
if (atOrBeyondPoint(startingPoint, destination, DY_PER_FRAME))
dy = -(startingPoint.y - destination.y);
else
dy = (textLocation == BELOW_COMPONENT ? -DY_PER_FRAME : DY_PER_FRAME);
break;
case SCROLLING_OUT:
destination = getTextStartingPoint();
if (atOrBeyondPoint(startingPoint, destination, DY_PER_FRAME))
dy = (destination.y - startingPoint.y);
else
dy = (textLocation == BELOW_COMPONENT ? DY_PER_FRAME : -DY_PER_FRAME);
break;
case DONE_SCROLLING_OUT:
case DONE_SCROLLING_IN:
dy = 0;
break;
}
return dy;
}
/**
* Utility method for drawNextFrame.
*
* Given the current location of the text, this method determines whether the
* scrollState should be set to SCROLLING_IN, SCROLLING_OUT, DONE_SCROLLING_IN, or
* DONE_SCROLLING_OUT.
* This method actually modifies the 'scrollState' member of this class.
*
* @see #drawNextFrame
*/
protected void updateDirection(Point currentPoint)
{
switch (scrollState)
{
case SCROLLING_IN:
if (atOrBeyondPoint(currentPoint, getTextFinishingPoint(), 0))
scrollState = DONE_SCROLLING_IN;
break;
case SCROLLING_OUT:
if (atOrBeyondPoint(currentPoint, getTextStartingPoint(), 0))
scrollState = DONE_SCROLLING_OUT;
break;
case DONE_SCROLLING_OUT:
case DONE_SCROLLING_IN:
break;
}
}
/**
* Utility method for updateDirection, calculateDX, and calculateDY.
*
* If paramter 'allowance' equals zero, this method determines whether or not the text
* has scrolled to a position which is at or beyond the point specified in the
* parameter 'point' (based on textLocation and scrollState).
*
* For a non-zero 'allowance', this method determines whether or not the text
* has scrolled to a position which is either beyond 'point' or within 'allowance'
* pixels of 'point' (based on the current textLocation and scrollState).
*
* @return whether or not 'currentLocation' is at or beyond 'point' (given 'allowance' pixels of leeway).
*/
protected boolean atOrBeyondPoint(Point currentLocation, Point point, int allowance)
{
if (currentLocation == null || point == null)
return false;
switch (textLocation)
{
case RIGHT_OF_COMPONENT:
switch (scrollState)
{
case SCROLLING_IN:
return currentLocation.x <= point.x + allowance;
case SCROLLING_OUT:
return currentLocation.x >= point.x - allowance;
}
case LEFT_OF_COMPONENT:
switch (scrollState)
{
case SCROLLING_IN:
return currentLocation.x >= point.x - allowance;
case SCROLLING_OUT:
return currentLocation.x <= point.x + allowance;
}
case ABOVE_COMPONENT:
switch (scrollState)
{
case SCROLLING_IN:
return currentLocation.y >= point.y - allowance;
case SCROLLING_OUT:
return currentLocation.y <= point.y + allowance;
}
case BELOW_COMPONENT:
switch (scrollState)
{
case SCROLLING_IN:
return currentLocation.y <= point.y + allowance;
case SCROLLING_OUT:
return currentLocation.y >= point.y - allowance;
}
}
return false;
}
/**
* Utility method for getTextStartingPoint and getTextFinishingPoint.
*
* This method calculates the y-position at which the text should be drawn if
* the textLocation is either LEFT_OF_COMPONENT or RIGHT_OF_COMPONENT. Requires
* a graphics context to be available.
*
* @return the y-position at which the text should be drawn if
* the textLocation is either LEFT_OF_COMPONENT or RIGHT_OF_COMPONENT.
* @see #getTextStartingPoint
* @see #getTextFinishingPoint
*/
protected int getTextConstantYPos()
{
return myComponent.getLocation().y + (myComponent.getSize().height + textAscent)/2;
}
/**
* Utility method for getTextStartingPoint and getTextFinishingPoint.
*
* This method calculates the x-position at which the text should be drawn if
* the textLocation is either ABOVE_COMPONENT or BELOW_COMPONENT. Requires
* a graphics context to be available.
*
* @return the x-position at which the text should be drawn if
* the textLocation is either ABOVE_COMPONENT or BELOW_COMPONENT.
* @see #getTextStartingPoint
* @see #getTextFinishingPoint
*/
protected int getTextConstantXPos()
{
return myComponent.getLocation().x + (myComponent.getSize().width - textWidth) / 2;
}
/**
* Utility method.
*
* This method calculates the ascent, descent, height, and width of the current
* text, based on the current graphics context.
*
*/
protected void calculateTextAttributes()
{
if (text == null)
return;
Graphics g = getGraphics();
if (g != null)
{
FontMetrics fm = g.getFontMetrics();
if (fm != null)
{
textAscent = fm.getAscent();
textDescent = fm.getDescent();
textHeight = textAscent + textDescent;
textWidth = fm.stringWidth(text);
}
}
}
/**
* Calculates the rectangle which bounds the portion of this component's background which
* the text will animate over.
*
* @return this bounding rectangle.
*/
protected Rectangle getBufferedBackgroundBounds()
{
if (myComponent != null)
{
if (textScrollsIn)
return getAreaAffectedByScroll();
else
return getAreaAffectedByPopup();
}
//The component has not yet been set, so return null
return null;
}
/**
* Utility method for getBufferedBackgroundBounds.
*
* Calculates the rectangle which bounds the portion of this component's background
* which the text will scroll over.
*
* @return this bounding rectangle (or null if there is insufficient space to do the
* animation.
*/
protected Rectangle getAreaAffectedByScroll()
{
int leftMostX = getBufferedBackgroundLeftMostX();
int rightMostX = getBufferedBackgroundRightMostX();
int topMostY = getBufferedBackgroundTopMostY();
int bottomMostY = getBufferedBackgroundBottomMostY();
if (rightMostX > leftMostX && topMostY < bottomMostY)
{
return new Rectangle(leftMostX - 1, topMostY - 1,
rightMostX - leftMostX + 1, bottomMostY - topMostY + 1);
// +1 and -1 to allow for drawing a rectangle around the text.
}
//The InfoTipPanel is not large enough for the user to
//see any animation, so return null
return null;
}
/**
* Utility method for getBufferedBackgroundBounds.
*
* Calculates the rectangle which bounds the portion of this component's background
* which the text will pop-up over.
*
* @return this bounding rectangle (or null if there is insufficient space to do the
* animation.
*/
protected Rectangle getAreaAffectedByPopup()
{
int leftMostX, rightMostX, topMostY, bottomMostY;
if (textLocation == RIGHT_OF_COMPONENT)
rightMostX = Math.min(getSize().width, getTextFinishingPoint().x + textWidth + (highlightText ? extraHighlightWidth/2 : 0));
else
rightMostX = getBufferedBackgroundRightMostX();
if (textLocation == LEFT_OF_COMPONENT)
leftMostX = Math.max(0, getTextFinishingPoint().x - (highlightText ? extraHighlightWidth/2 : 0));
else
leftMostX = getBufferedBackgroundLeftMostX();
if (textLocation == ABOVE_COMPONENT)
topMostY = Math.max(0, getTextFinishingPoint().y - textAscent - (highlightText ? extraHighlightHeight/2 : 0));
else
topMostY = getBufferedBackgroundTopMostY();
if (textLocation == BELOW_COMPONENT)
bottomMostY = Math.min(getSize().height, getTextFinishingPoint().y + textDescent + (highlightText ? extraHighlightHeight/2 : 0));
else
bottomMostY = getBufferedBackgroundBottomMostY();
if (rightMostX > leftMostX && topMostY < bottomMostY)
{
return new Rectangle(leftMostX - 1, topMostY - 1,
rightMostX - leftMostX + 1, bottomMostY - topMostY + 1);
// +1 and -1 to allow for drawing a rectangle around the text.
}
//The InfoTipPanel is not large enough for the user to
//see any animation, so return null
return null;
}
/**
* Utility method for getBufferedBackgroundBounds.
*
* This method calculates the y-coordinate of the rectangle which bounds the portion
* of this component's background which the text will animate over.
*
* @return this y-coordinate.
* @see #getBufferedBackgroundBounds
* @see #getBufferedBackgroundBottomMostY
* @see #getBufferedBackgroundLeftMostX
* @see #getBufferedBackgroundRightMostX
*/
protected int getBufferedBackgroundTopMostY()
{
int topMostY = 0;
switch (textLocation)
{
case RIGHT_OF_COMPONENT:
case LEFT_OF_COMPONENT:
topMostY = getTextConstantYPos() - textAscent - (highlightText ? extraHighlightHeight/2 : 0);
break;
case ABOVE_COMPONENT:
topMostY = 0;
break;
case BELOW_COMPONENT:
topMostY = (myComponent.getLocation().y + myComponent.getSize().height + PIXEL_BUFFER - (highlightText ? extraHighlightHeight/2 : 0));
break;
}
return topMostY;
}
/**
* Utility method for getBufferedBackgroundBounds.
*
* This method calculates the y-coordinate of the bottom edge of the rectangle
* which bounds the portion of this component's background which the text will
* animate over.
*
* @return this y-coordinate.
* @see #getBufferedBackgroundBounds
* @see #getBufferedBackgroundTopMostY
* @see #getBufferedBackgroundLeftMostX
* @see #getBufferedBackgroundRightMostX
*/
protected int getBufferedBackgroundBottomMostY()
{
int bottomMostY = 0;
switch (textLocation)
{
case RIGHT_OF_COMPONENT:
case LEFT_OF_COMPONENT:
bottomMostY = getTextConstantYPos() + textDescent + (highlightText ? extraHighlightHeight/2 : 0);
break;
case ABOVE_COMPONENT:
bottomMostY = myComponent.getLocation().y - PIXEL_BUFFER + textDescent + (highlightText ? extraHighlightHeight/2 : 0);
break;
case BELOW_COMPONENT:
bottomMostY = getSize().height;
break;
}
return bottomMostY;
}
/**
* Utility method for getBufferedBackgroundBounds.
*
* This method calculates the x-coordinate of the rectangle which bounds
* the portion of this component's background which the text will animate over.
*
* @return this x-coordinate.
* @see #getBufferedBackgroundBounds
* @see #getBufferedBackgroundTopMostY
* @see #getBufferedBackgroundBottomMostY
* @see #getBufferedBackgroundRightMostX
*/
protected int getBufferedBackgroundLeftMostX()
{
int xPos = 0;
switch (textLocation)
{
case RIGHT_OF_COMPONENT:
xPos = getTextFinishingPoint().x - (highlightText ? extraHighlightWidth/2 : 0);
break;
case LEFT_OF_COMPONENT:
xPos = 0;
break;
case ABOVE_COMPONENT:
case BELOW_COMPONENT:
xPos = getTextConstantXPos() - (highlightText ? extraHighlightWidth/2 : 0);
break;
}
return xPos;
}
/**
* Utility method for getBufferedBackgroundBounds.
*
* This method calculates the x-coordinate of the right edge of the rectangle
* which bounds the portion of this component's background which the text will
* animate over.
*
* @return this x-coordinate.
* @see #getBufferedBackgroundBounds
* @see #getBufferedBackgroundTopMostY
* @see #getBufferedBackgroundBottomMostY
* @see #getBufferedBackgroundLeftMostX
*/
protected int getBufferedBackgroundRightMostX()
{
int xPos = 0;
switch (textLocation)
{
case RIGHT_OF_COMPONENT:
xPos = getSize().width;
break;
case LEFT_OF_COMPONENT:
xPos = myComponent.getLocation().x - PIXEL_BUFFER + (highlightText ? extraHighlightWidth/2 : 0);
break;
case ABOVE_COMPONENT:
case BELOW_COMPONENT:
xPos = getTextConstantXPos() + textWidth + (highlightText ? extraHighlightWidth/2 : 0);
break;
}
return xPos;
}
/**
* Calculates the point at which the text should be located when the scrolling
* animation is initiated.
*
* @return this Point
* @see #getTextFinishingPoint
*/
protected Point getTextStartingPoint()
{
Point startingPoint = new Point(0,0);
switch (textLocation)
{
case RIGHT_OF_COMPONENT:
startingPoint.x = getSize().width + (highlightText ? extraHighlightWidth/2 : 0);
startingPoint.y = getTextConstantYPos();
break;
case LEFT_OF_COMPONENT:
startingPoint.x = -textWidth - (highlightText ? extraHighlightWidth/2 : 0);
startingPoint.y = getTextConstantYPos();
break;
case ABOVE_COMPONENT:
startingPoint.x = getTextConstantXPos();
startingPoint.y = -textDescent - (highlightText ? extraHighlightHeight/2 : 0);
break;
case BELOW_COMPONENT:
startingPoint.x = getTextConstantXPos();
startingPoint.y = getSize().height + textAscent + (highlightText ? extraHighlightHeight/2 : 0);
break;
}
return startingPoint;
}
/**
* Calculates the point at which the text should be located when the scrolling
* animation has scrolled the text all the way in.
*
* @return this Point
* @see #getTextStartingPoint
*/
protected Point getTextFinishingPoint()
{
Point finishingPoint = new Point(0,0);
switch (textLocation)
{
case RIGHT_OF_COMPONENT:
finishingPoint.x = myComponent.getLocation().x + myComponent.getSize().width + PIXEL_BUFFER;
finishingPoint.y = getTextConstantYPos();
break;
case LEFT_OF_COMPONENT:
finishingPoint.x = myComponent.getLocation().x - PIXEL_BUFFER - textWidth;
finishingPoint.y = getTextConstantYPos();
break;
case ABOVE_COMPONENT:
finishingPoint.x = getTextConstantXPos();
finishingPoint.y = myComponent.getLocation().y - PIXEL_BUFFER;
break;
case BELOW_COMPONENT:
finishingPoint.x = getTextConstantXPos();
finishingPoint.y = myComponent.getLocation().y + myComponent.getSize().height + PIXEL_BUFFER + textAscent;
break;
}
return finishingPoint;
}
/**
* Adds the specified component to this container at the specified
* index. Also notifies the layout manager to add the component to
* the this container's layout using the specified constraints object.
* <p>
* This is the method to override if you want to track every add
* request to a container. An overriding method should usually
* include a call to super.addImpl(comp, constraints, index).
*
* It is overridden here to set the only
* component in this InfoTipPanel. This is the component
* that triggers text scrolling on a mouse entered event.
* The InfoTipPanel can only contain one component. Any
* previous component will be removed before the new one is added.
*
* @param comp the component to be added
* @param constraints an object expressing layout contraints for this
* component
* @param index the position in the container's list at which to
* insert the component. -1 means insert at the end.
* @see #remove
*/
protected void addImpl(Component comp, Object constraints, int index)
{
if (callingAddInternally)
{
//if we are calling add from Within InfoTipPanel do a regular old addImpl()
super.addImpl(comp,constraints,index);
return;
}
setComponent(comp);
}
/**
* This method re-creates the off screen images necessary for the text animation.
* This method must be called any time that the sizes of these images must be
* re-calculated -- it will flush() the old images (if any) and create new images
* of the correct size.
*/
protected void createOffScreenBuffers()
{
if (bufferedBgndImage != null)
{
bufferedBgndImage.flush();
bufferedBgndImage = null;
}
if (offScreenImage != null)
{
offScreenImage.flush();
offScreenImage = null;
}
Rectangle bufferedBgndImageBounds = getBufferedBackgroundBounds();
if (bufferedBgndImageBounds != null)
{
bufferedBgndImage = createImage(bufferedBgndImageBounds.width, bufferedBgndImageBounds.height);
offScreenImage = createImage(bufferedBgndImageBounds.width, bufferedBgndImageBounds.height);
}
storeBackground();
}
/**
* Standard method which handles serializing this object.
* It has been OVERRIDDEN here to re-calculate all of the
* state which is marked transient (including scrollState,
* sizes, and offScreenBuffers).
*/
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException
{
in.defaultReadObject();
scrollState = DONE_SCROLLING_OUT;
calculateTextAttributes();
createOffScreenBuffers();
}
/**
* This internal variable represents the state of the current text-scrolling
* animation, if any.
* Should be one of: SCROLLING_IN, DONE_SCROLLING_IN, SCROLLING_OUT,
* or DONE_SCROLLING_OUT.
*/
transient int scrollState;
/**
* This property represents the direction from which the text should be
* scrolled by the InfoTipPanel.
* Should be one of: RIGHT_OF_COMPONENT, LEFT_OF_COMPONENT, ABOVE_COMPONENT,
* or BELOW_COMPONENT.
* @see #setTextLocation
* @see #getTextLocation
*/
int textLocation;
/**
* The distance between the edge of the added component and the point at which the text stops scrolling.
* @see #getTextFinishingPoint
*/
static final int PIXEL_BUFFER = 10;
/**
* The horizontal distance which the text moves each animation frame (if the text is scrolling left or right).
* @see #drawNextFrame
*/
static final int DX_PER_FRAME = 25;
/**
* The vertical distance which the text moves each animation frame (if the text is scrolling up or down).
* @see #drawNextFrame
*/
static final int DY_PER_FRAME = 17;
/**
* The time (in milliseconds) paused between each animation frame.
* @see #scrollOneDirection
*/
static final int TIME_BETWEEN_FRAMES = 50;
/**
* The thread which will animate the scrolling text.
*/
transient protected Thread textAnimationThread = null;
/**
* Off screen buffer used for animation.
*/
transient protected Image offScreenImage = null;
/**
* Off screen buffer used for animation.
*/
transient protected Image bufferedBgndImage = null;
/**
* Internal flag that indicates a call to add() is being made from within InfoTipPanel.
*/
transient protected boolean callingAddInternally = false;
/**
* Internal flag that indicates that the component is NOT to draw the text from within paint().
*/
transient protected boolean dontDrawText = false;
/**
* Width of current message text.
*/
transient protected int textWidth;
/**
* Height of current message text.
*/
transient protected int textHeight;
/**
* Ascent of current message text.
*/
transient protected int textAscent;
/**
* Descent of current message text.
*/
transient protected int textDescent;
/**
* Reference to the component which will be added to the InfoTipPanel and
* whose mouse entered and mouse exited events will control the scrolling of the text.
* @see #setComponent
* @see #getComponent
*/
protected Component myComponent = null;
/**
* The text which will be scrolled on mouse entered and mouse exited events from myComponent.
* @see #setText
* @see #getText
*/
protected String text = null;
/**
* boolean property representing whether the text will be scrolled in from the
* edge of the panel on a mouse over, or popped up beside the component.
* @see #setTextScrollsIn
* @see #getTextScrollsIn
*/
protected boolean textScrollsIn = true;
/**
* int property which represents how long (in milliseconds) to pause before
* starting to scroll/pop-up text on a mouse entered event.
* @see #setTextDelay
* @see #getTextDelay
*/
protected int textDelay = 0;
/**
* boolean property representing whether the text will be drawn transparently
* rather than using the specified textHighlightColor.
* @see #setHighlightText
* @see #getHighlightText
*/
protected boolean highlightText = false;
/**
* The color used for text when this component is enabled.
* @see #getTextColor
* @see #setTextColor
*/
protected Color textColor = null;
/**
* Color which will be used to highlight the text if highlightText flag is set.
* @see #setTextHighlightColor
* @see #getTextHighlightColor
*/
protected Color textHighlightColor = null;
/**
* Timer object which is used to time the delay before popping up the text.
*/
protected Timer delayTimer = null;
/**
* Reference to the Mouse object which is listening to myComponent for mouse events.
*/
protected Mouse myMouseListener = null;
/**
* Extra width which is added on if highlighText == true
*/
protected int extraHighlightWidth;
/**
* Extra height which is added on if highlighText == true
*/
protected int extraHighlightHeight;
/**
* Defines the "scrolling in" scroll state. One of four possible values for scrollState.
*/
protected static final int SCROLLING_IN = 0;
/**
* Defines the "done scrolling in" scroll state. One of four possible values for scrollState.
*/
protected static final int DONE_SCROLLING_IN = 1;
/**
* Defines the "scrolling out" scroll state. One of four possible values for scrollState.
*/
protected static final int SCROLLING_OUT = 2;
/**
* Defines the "done scrolling out" scroll state. One of four possible values for scrollState.
*/
protected static final int DONE_SCROLLING_OUT = 3;
/**
* Defines the "waiting to scroll in" scroll state. One of four possible values for scrollState.
*/
protected static final int WAITING_TO_SCROLL_IN = 4;
/**
* Error strings.
*/
transient protected ResourceBundle errors;
// Private members
private LocationVeto locationVeto = null;
private symantec.itools.beans.VetoableChangeSupport vetos = new symantec.itools.beans.VetoableChangeSupport(this);
private symantec.itools.beans.PropertyChangeSupport changes = new symantec.itools.beans.PropertyChangeSupport(this);
}