home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 1998 November
/
Chip_1998-11_cd.bin
/
tema
/
Cafe
/
Source.bin
/
ScrollingText.java
< prev
next >
Wrap
Text File
|
1998-03-18
|
28KB
|
1,044 lines
package symantec.itools.multimedia;
import java.awt.*;
import java.awt.event.*;
import java.applet.Applet;
import java.applet.AppletContext;
import java.net.URL;
import java.beans.PropertyVetoException;
import java.beans.PropertyChangeListener;
import java.beans.VetoableChangeListener;
import java.util.ResourceBundle;
/**
* Scrolling text component. Forms a text banner that scrolls specified
* text horizontally. The component allows multiple messages and links.
*
* @version 1.0, Feb 2, 1997
* @author Symantec
*/
// 02/02/97 RKM Checked it in
// 02/07/97 RKM Fixed bug in paint where the font was noit being set up in the offscreen image (thanks Dave)
// 05/31/97 RKM Updated to support Java 1.1
// Converted events
// Made properties bound & constrained
// Made choice to have a little more code in saving old & new properties when firing, but
// think the code is cleaner
// Changed updateCurrentMessage & nextPos to protected
// 07/17/97 CAR marked fields transient as needed
// 07/17/97 CAR changed class Mouse to implement java.io.Serializable interface
// 07/17/97 CAR initialized messageList in default constructor
// 08/21/97 LAB Made paint use the same image unless the size changes ala ButtonBase
// (faster, and fixes a Win bug).
public class ScrollingText extends Canvas implements Runnable
{
// Enums
/**
* Constant which indicates that the banner should scroll from right to left
*/
public static final int SCROLL_LEFT = 0;
/**
* Constant which indicates that the banner should scroll from left to right
*/
public static final int SCROLL_RIGHT = 1;
// Property variables
/**
* Current scroll direction. Default value SCROLL_LEFT.
*/
protected int scrollDirection;
/**
* Distance to scroll on each update. Distance is in pixels. Default value 10.
*/
protected int scrollUnit; //the incremental scrolling distance in pixels
/**
* Controls the speed of the scroll. Default value 150.
*/
protected int sleepTime; //controls the speed of the scroll
/**
* Color for highlighted text. Default value is red.
*/
protected Color hiliteColor;
/**
* List of messages to scroll across the banner.
*/
protected String[] messageList;
/**
* URL links which correspond with messages.
*/
protected URL[] linkToList;
/**
* Location in browser to show linked HTML pages.
* Valid values are "_self" to show in the current frame;
* "_parent" show in the parent frame;
* "_top" show in the topmost frame;
* "_blank" show in a new unnamed top-level window;
* name show in a new top-level window named name.
*/
protected String frame;
// Internal variables
/**
* If used in an applet, this is the applet's context.
*/
transient protected AppletContext context;
/**
* Thread which runs the scrolling animation.
*/
transient protected Thread scrollThread = null;
/**
* State of scrolling animation.
*/
transient protected boolean suspended = false;
/**
* Current horizontal text position.
*/
transient protected int textX;
/**
* Current vertical text position.
*/
transient protected int textY;
/**
* Width of current message text.
*/
transient protected int textWidth;
/**
* Height of current message text.
*/
transient protected int textHeight;
/**
* Mouse over message text.
*/
transient protected boolean isMouseOver = false;
/**
* Last mouse cursor position.
*/
transient protected int lastMouseX,lastMouseY;
/**
* Status of mouse position.
*/
transient protected boolean wasMouseOverText = false;
/**
* Previous status message.
*/
transient protected String wasStatusMessage = "";
/**
* Current message text.
*/
protected String currMessage;
/**
* Current URL link.
*/
protected URL currLinkTo;
/**
* Index of current message text and URL
*/
protected int currIndex;
// Constructor
/**
* Construct default scrolling text banner.
*/
public ScrollingText()
{
super();
//Set defaults for properties
scrollDirection = SCROLL_LEFT;
scrollUnit = 10;
sleepTime = 150;
hiliteColor = Color.red;
//messageList = new String[0];
ResourceBundle res = ResourceBundle.getBundle("symantec.itools.resources.ResBundle");
messageList = new String[5];
messageList[0] = res.getString("put");
messageList[1] = res.getString("some");
messageList[2] = res.getString("text");
messageList[3] = res.getString("in");
messageList[4] = res.getString("here");
linkToList = new URL[0];
frame = null;
//Init current message variables
currMessage = "";
currLinkTo = null;
currIndex = -1;
vetos = new symantec.itools.beans.VetoableChangeSupport(this);
changes = new symantec.itools.beans.PropertyChangeSupport(this);
}
//
// Getters/Setters
//
/**
* Set sleep time between scroll steps. This function controls
* the speed of scrolling. A lower number indicates a faster speed.
*
* @param speed number of milliseconds to sleep between scroll steps. The smaller the
* number the faster the scroll. Minimum value of 30.
*
* @exception PropertyVetoException
* if the specified property value is unacceptable
*/
public void setScrollInterval(int speed)
throws PropertyVetoException
{
//???RKM??? Would an exception be better here
if (speed < 30)
speed = 30;
if (speed != sleepTime)
{
Integer oldScrollIntervalInt = new Integer(sleepTime);
Integer newScrollIntervalInt = new Integer(speed);
vetos.fireVetoableChange("ScrollInterval", oldScrollIntervalInt, newScrollIntervalInt);
sleepTime = speed;
changes.firePropertyChange("ScrollInterval", oldScrollIntervalInt, newScrollIntervalInt);
}
}
/**
* Obtain the current scroll interval. This is the number of milliseconds
* that the scroll thread will sleep between scroll steps.
*
* @return current scroll delay setting, in milliseconds
*/
public int getScrollInterval()
{
return sleepTime;
}
/**
* Specify the size of each scroll step.
*
* @param unit the number of pixels to move the text on each scroll step
*
* @exception PropertyVetoException
* if the specified property value is unacceptable
*/
public void setScrollUnit(int unit)
throws PropertyVetoException
{
//???RKM??? Would an exception be better here
if (unit < 1)
unit = 1;
if (scrollUnit != unit)
{
Integer oldScrollUnitInt = new Integer(scrollUnit);
Integer newScrollUnitInt = new Integer(unit);
vetos.fireVetoableChange("ScrollUnit", oldScrollUnitInt, newScrollUnitInt);
scrollUnit = unit;
changes.firePropertyChange("ScrollUnit", oldScrollUnitInt, newScrollUnitInt);
}
}
/**
* Obtain the current size of the scroll step.
*
* @return the number of pixels that text moves on each scroll step
*
*/
public int getScrollUnit()
{
return scrollUnit;
}
/**
* Specify the banner's current scroll direction.
*
* @param dir direction to scroll. Valid values are SCROLL_LEFT and SCROLL_RIGHT
*
* @exception PropertyVetoException
* if the specified property value is unacceptable
*/
public void setScrollDirection(int dir)
throws PropertyVetoException
{
//???RKM??? Would an exception be better here
if (dir != SCROLL_LEFT && dir != SCROLL_RIGHT)
dir = SCROLL_RIGHT;
if (scrollDirection != dir)
{
Integer oldScrollDirectionInt = new Integer(scrollDirection);
Integer newScrollDirectionInt = new Integer(dir);
vetos.fireVetoableChange("ScrollDirection", oldScrollDirectionInt, newScrollDirectionInt);
scrollDirection = dir;
changes.firePropertyChange("ScrollDirection", oldScrollDirectionInt, newScrollDirectionInt);
}
}
/**
* Obtain the current direction of scrolling.
*
* @return the current direction, either SCROLL_LEFT or SCROLL_RIGHT
*/
public int getScrollDirection()
{
return scrollDirection;
}
/**
* Set the highlight color for text. Text is highlighted in this color when the mouse cursor is over
* it and it contains a non-null link.
*
* @param newHiliteColor color for highlighted text.
*
* @exception PropertyVetoException
* if the specified property value is unacceptable
*/
public void setHiliteColor(Color newHiliteColor)
throws PropertyVetoException
{
if (!hiliteColor.equals(newHiliteColor))
{
Color oldColor = hiliteColor;
Color newColor = newHiliteColor;
vetos.fireVetoableChange("HiliteColor", oldColor, newColor);
hiliteColor = newHiliteColor;
changes.firePropertyChange("HiliteColor", oldColor, newColor);
}
}
/**
* Obtain the current color for text highlighting
*
* @return current highlight color
*/
public Color getHiliteColor()
{
return hiliteColor;
}
/**
* Specify the current list of messages. These messages scroll sequentially
* through the banner.
*
* @param list list of messages to display
*
* @exception PropertyVetoException
* if the specified property value is unacceptable
*/
public void setMessageList(String[] list)
throws PropertyVetoException
{
if (messageList != list)
{
String[] oldMessageList = messageList;
String[] newMessageList = list;
currIndex = 0;
vetos.fireVetoableChange("MessageList",oldMessageList,newMessageList);
messageList = list;
changes.firePropertyChange("MessageList",oldMessageList,newMessageList);
updateCurrentMessage(false);
}
}
/**
* Obtain the current list of messages.
*
* @return the current list of messages being displayed
*/
public String[] getMessageList()
{
return messageList;
}
/**
* Specify the list of URLs for message links.
* Each link corresponds with a message in the message list.
* If a message has no link, a null URL should be included for that message.
*
* @param list list of URL links
*
* @exception PropertyVetoException
* if the specified property value is unacceptable
*/
public void setLinkToList(URL[] list)
throws PropertyVetoException
{
if (linkToList != list)
{
URL[] oldLinkToList = linkToList;
URL[] newLinkToList = list;
currIndex = 0;
vetos.fireVetoableChange("LinkToList",oldLinkToList,newLinkToList);
linkToList = list;
changes.firePropertyChange("LinkToList",oldLinkToList,newLinkToList);
updateCurrentMessage(false);
}
}
/**
* Obtain the current set of URL links.
*
* @return list of URL links. Each URL corresponds to the message of the same index in the message list
*/
public URL[] getLinkToList()
{
return linkToList;
}
/**
* Specify the display location of linked pages. If the ScrollingText is in an applet this
* method determines where the linked pages are displayed.
*
* @param newFrame where to display the linked documents.
* Valid values are "_self" to show in the current frame;
* "_parent" show in the parent frame;
* "_top" show in the topmost frame;
* "_blank" show in a new unnamed top-level window;
* <name> show in a new top-level window named name
*
* @exception PropertyVetoException
* if the specified property value is unacceptable
*/
public void setFrame(String newFrame)
throws PropertyVetoException
{
if (!symantec.itools.util.GeneralUtils.objectsEqual(frame,newFrame))
{
String oldFrame = frame;
vetos.fireVetoableChange("Frame",oldFrame,newFrame);
frame = newFrame;
changes.firePropertyChange("Frame",newFrame,newFrame);
}
}
/**
* Obtain the display location of linked pages. If the ScrollingText is in an applet this
* method returns where the linked pages are displayed.
*
* @return Place to display the linked documents.
* Valid values are "_self" to show in the current frame;
* "_parent" show in the parent frame;
* "_top" show in the topmost frame;
* "_blank" show in a new unnamed top-level window;
* <name> show in a new top-level window named name
*/
public String getFrame()
{
return frame;
}
// Overridden methods
/**
* 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 start the scrolling text thread.
*
* @see #removeNotify
*/
public synchronized void addNotify()
{
super.addNotify();
//Hook up listeners
if (mouse == null)
{
mouse = new Mouse();
addMouseListener(mouse);
}
if (mouseMotion == null)
{
mouseMotion = new MouseMotion();
addMouseMotionListener(mouseMotion);
}
//Start thread
scrollThread = new Thread(this);
scrollThread.setPriority(Thread.MIN_PRIORITY);
scrollThread.start();
}
/**
* 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 stop the scrolling text thread.
*
* @see #addNotify
*/
public synchronized void removeNotify()
{
//Stop thread
if (scrollThread != null)
{
scrollThread.stop();
scrollThread = null;
}
//Unhook listeners
if (mouse != null)
{
removeMouseListener(mouse);
mouse = null;
}
if (mouseMotion != null)
{
removeMouseMotionListener(mouseMotion);
mouseMotion = null;
}
super.removeNotify();
}
/**
* Resumes the animation of the scrolling text.
*/
public void startScrollingText() {
suspended = false;
show();
}
/**
* Suspends the animation of the scrolling text.
*/
public void stopScrollingText() {
suspended = true;
}
/**
* Makes this component visible.
* This is a standard Java AWT method which gets called to show this
* component. If this component was invisible due to a previous hide()
* call it make this component visible again.
*
* @see #hide
*/
public synchronized void show() {
super.show();
if (isVisible()) {
if (scrollThread != null) {
scrollThread.setPriority(Thread.MAX_PRIORITY);
scrollThread.resume();
}
}
}
/**
* Makes this component invisible.
* This is a standard Java AWT method which gets called to hide
* this component. A hidden component cannot be seen by the user nor
* does it take up space in its container, but it does continue to
* exist.
*
* @see #show
*/
public synchronized void hide() {
super.hide();
if (!isVisible()) {
if (scrollThread != null)
scrollThread.suspend();
}
}
/**
* ScrollingText thread body. This method is called by the Java virtual
* machine to when this thread starts.
*/
public void run()
{
createTextParams();
while (true)
{
if (!suspended)
{
nextPos();
try
{
Thread.sleep(sleepTime);
}
catch(Exception e)
{
}
}
}
}
/**
* Move to next message in message list. Not usually called directly.
*
* @param next if true, move to next message; otherwise reset message with current index
*/
protected void updateCurrentMessage(boolean next)
{
//Increase currIndex, try to get message out
try
{
if (next)
currIndex++;
if(currIndex >= messageList.length)
{
currIndex = 0;
}
currMessage = messageList[currIndex];
}
catch(ArrayIndexOutOfBoundsException e)
{
//Index is out of range, reset to zero
try
{
currIndex = 0;
currMessage = messageList[0];
}
catch(ArrayIndexOutOfBoundsException e2)
{
//No index is valid at this point
currMessage = "";
}
}
if(currIndex < linkToList.length)
{
currLinkTo = linkToList[currIndex];
}
else
{
currLinkTo = null;
}
/*
// Get current link to, if one is there
try
{
currLinkTo = linkToList[currIndex];
}
catch(ArrayIndexOutOfBoundsException e)
{
currLinkTo = null;
}
*/
createTextParams();
}
/**
* Increment scroll step. Moves to next message as needed. Not usually called directly.
*/
protected synchronized void nextPos()
{
Dimension dim = size();
if (scrollDirection == SCROLL_LEFT)
{
textX -= scrollUnit;
if ((textX + textWidth) < 0)
{
updateCurrentMessage(true);
textX = dim.width;
}
}
else
{
textX += scrollUnit;
if (textX > dim.width)
{
updateCurrentMessage(true);
textX = -textWidth;
}
}
repaint();
}
/**
* Setup metrics information for current message. Sets textX, textY, textHeight and textWidth.
*/
protected void createTextParams()
{
Font f = getFont();
if (f != null)
{
FontMetrics fm = getFontMetrics(f);
textHeight = fm.getHeight();
Dimension dim = size();
textX = dim.width;
textY = ((dim.height - textHeight) >> 1) + fm.getAscent();
textWidth = fm.stringWidth(currMessage);
}
}
/**
* Handles redrawing of this component on the screen.
* This is a standard Java AWT method which gets called by the Java
* AWT (repaint()) to handle repainting this component on the screen.
* 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.
* Typically this method paints the background color to clear the
* component's drawing space, sets graphics context to be the foreground
* color, and then calls paint() to draw the component.
*
* It is overridden here to reduce flicker by eliminating the uneeded
* clearing of the background.
*
* @param g the graphics context
* @see java.awt.Component#repaint
* @see #paint
*/
public void update(Graphics g) {
paint(g);
}
/**
* Determines if the given point is over the current text.
*
* @param x horizontal location of point
* @param y vertical location of point
*
* @return true if given point is over the current text
*/
protected boolean isMouseOverText(int x, int y)
{
if (isMouseOver)
{
if (x >= textX && x <= textX + textWidth)
{
if (y >= textY - textHeight && y <= textY)
return true;
}
}
return false;
}
/**
* 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.
*
* @param g the graphics context used for painting
* @see java.awt.Component#repaint
* @see #update
*/
public void paint(Graphics g)
{
Dimension dim = size();
if(isImageInvalid())
{
textImage = createImage(dim.width, dim.height);
try
{
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(textImage, 0);
tracker.waitForID(0);
}
catch(InterruptedException e){}
}
Graphics textGC = textImage.getGraphics();
//Draw the background color
textGC.setColor(getBackground());
textGC.fillRect(0, 0, dim.width, dim.height);
textGC.setFont(getFont());
//Determine where the mouse is
boolean mouseOverText = isMouseOverText(lastMouseX, lastMouseY);
if (mouseOverText != wasMouseOverText)
{
if (context != null)
{
String newStatusMessage;
if (wasMouseOverText || currLinkTo == null)
newStatusMessage = "";
else
newStatusMessage = currLinkTo.toString();
//Update status, only if something has changed
if (!wasStatusMessage.equals(newStatusMessage))
{
context.showStatus(newStatusMessage);
wasStatusMessage = newStatusMessage;
}
}
wasMouseOverText = mouseOverText;
}
//Draw the text
textGC.setColor(mouseOverText && currLinkTo != null ? hiliteColor : getForeground());
textGC.drawString(currMessage, textX, textY);
g.drawImage(textImage, 0, 0, this);
if (textGC != null)
textGC.dispose();
}
class MouseMotion extends MouseMotionAdapter implements java.io.Serializable
{
//???RKM??? Revist JavaDoc
/**
* Processes MOUSE_MOVE events.
* This is a standard Java AWT method which gets called by the AWT
* method handleEvent() in response to receiving a MOUSE_MOVE
* event. These events occur when the mouse is moved around inside this
* component while the button is NOT pressed.
*
* @param evt the event
* @param x the component-relative horizontal coordinate of the mouse
* @param y the component-relative vertical coordinate of the mouse
*
* @return always true since the event was handled
*
* @see java.awt.Component#mouseDrag
* @see java.awt.Component#handleEvent
*/
public void mouseMoved(MouseEvent event) {
isMouseOver = true;
//Use this later to determine if the mouse is over the text
lastMouseX = event.getX();
lastMouseY = event.getY();
}
}
class Mouse extends MouseAdapter implements java.io.Serializable
{
/**
* Processes MOUSE_EXIT events.
* This is a standard Java AWT method which gets called by the AWT
* method handleEvent() in response to receiving a MOUSE_EXIT
* event. These events occur when the mouse first leaves this
* component.
*
* @param e the event
* @param x the component-relative horizontal coordinate of the mouse
* @param y the component-relative vertical coordinate of the mouse
*
* @return always true since the event was handled
*
* @see java.awt.Component#mouseEnter
* @see java.awt.Component#handleEvent
*/
public void mouseExited(MouseEvent evt) {
isMouseOver = false;
}
/**
* Processes MOUSE_DOWN events.
* This is a standard Java AWT method which gets called by the AWT
* method handleEvent() in response to receiving a MOUSE_DOWN
* event. These events occur when the mouse button is pressed while
* inside this component.
*
* @param e the event
* @param x the component-relative horizontal coordinate of the mouse
* @param y the component-relative vertical coordinate of the mouse
*
* @return always true since the event was handled
*
* @see java.awt.Component#mouseUp
* @see java.awt.Component#handleEvent
*/
public void mousePressed(MouseEvent evt) {
//If we have somewhere to go and the mouse is over the text
if (currLinkTo != null && isMouseOverText(evt.getX(), evt.getY()))
{
if (context != null)
{
if (frame == null || frame.length() == 0)
context.showDocument(currLinkTo);
else
context.showDocument(currLinkTo,frame);
}
}
}
}
/**
* Ensures that this component is laid out properly, as needed.
* This is a standard Java AWT method which gets called by the AWT to
* make sure this component and its subcomponents have a valid layout.
* If this component was made invalid with a call to invalidate(), then
* it is laid out again.
*
* It is overridden here to also find the containing applet at validation time.
*
* @see java.awt.Component#invalidate
*/
public void validate()
{
// On validation, try to find the containing applet. If we can find
// it, we don't bother doing the link...
Container c = getParent();
while (c != null)
{
if (c instanceof Applet)
{
setAppletContext(((Applet) c).getAppletContext());
break;
}
c = c.getParent();
}
}
/**
* Specify the current applet context.
*
* @param c new applet context
*/
//???RKM??? Make certain this doesn't show up as a property
protected void setAppletContext(AppletContext c)
{
context = c;
}
/**
* Moves and/or resizes this component.
* This is a standard Java AWT method which gets called to move and/or
* resize this component. Components that are in containers with layout
* managers should not call this method, but rely on the layout manager
* instead.
*
* @param x horizontal position in the parent's coordinate space
* @param y vertical position in the parent's coordinate space
* @param width the new width
* @param height the new height
*/
public synchronized void reshape(int x, int y, int width, int height) {
super.reshape(x,y,width,height);
createTextParams();
}
/**
* Adds a listener for all event changes.
* @param PropertyChangeListener listener the listener to add.
* @see #removePropertyChangeListener
*/
public void addPropertyChangeListener(PropertyChangeListener listener)
{
//super.addPropertyChangeListener(listener);
changes.addPropertyChangeListener(listener);
}
/**
* Removes a listener for all event changes.
* @param PropertyChangeListener listener the listener to remove.
* @see #addPropertyChangeListener
*/
public void removePropertyChangeListener(PropertyChangeListener listener)
{
//super.removePropertyChangeListener(listener);
changes.removePropertyChangeListener(listener);
}
/**
* Adds a vetoable listener for all event changes.
* @param VetoableChangeListener listener the listener to add.
* @see #removeVetoableChangeListener
*/
public void addVetoableChangeListener(VetoableChangeListener listener)
{
//super.addVetoableChangeListener(listener);
vetos.addVetoableChangeListener(listener);
}
/**
* Removes a vetoable listener for all event changes.
* @param VetoableChangeListener listener the listener to remove.
* @see #addVetoableChangeListener
*/
public void removeVetoableChangeListener(VetoableChangeListener listener)
{
//super.removeVetoableChangeListener(listener);
vetos.removeVetoableChangeListener(listener);
}
/**
* Returns true if a image has been set, but it is not the
* size of this component.
*/
protected boolean isImageInvalid()
{
Dimension s = getSize();
return (textImage == null || s.width != textImage.getWidth(this) || s.height != textImage.getHeight(this));
}
/**
* The offscreen buffer to draw in.
*/
transient protected Image textImage = null;
private symantec.itools.beans.VetoableChangeSupport vetos;
private symantec.itools.beans.PropertyChangeSupport changes;
private Mouse mouse = null;
private MouseMotion mouseMotion = null;
}