NewsScroller: NewsMessage.java


package crackers.Scroller;

/**
* This class represents an individual message item in the NewsScroller applet.
* This way, each message can take care of it's own position vs. the applet and
* draw itself.
* <P>
* Each message may contain a URL (and optional target, as found in <A HREF>
* HTML tags) to jump to when it is clicked upon.
* <P>
* This program is copyrighted 1999 by the author under the GNU Public License
* (GPL). Full text of this license is available at
* <A HREF="http://www.gnu.org">GNU.org</A>.
* <P>
* For full usage information, see
* <A HREF="http://www.io.com/~crackers">my web pages</A>.
*
* @author E. A. Graham, Jr. (crackers@io.com)
* @version 2.0
* @see NewsScroller
*/

import java.awt.*;
import java.applet.*;
import java.util.*;
import java.io.*;
import java.net.URL;
import java.net.MalformedURLException;

class NewsMessage {
	/**
	* The current top position of this message (in pixels).
	*/
	private int top;
	/**
	* The height of this message (in pixels).
	*/
	private int height;
	/**
	* The maximum width of a line (in pixels). Used for calculating the
	* wrapping of lines.
	*/
	private int maxWidth = 0;
	/**
	* A URL associated with this message, if any.
	*/
	private URL link = null;
	/**
	* A URL target, if any.
	*/
	private String target = null;
	/**
	* The font for headlines.
	*/
	private Font headFont;
	/**
	* The fontmetric for headlines. Pre-calculated to save those previous
	* milliseconds.
	*/
	private FontMetrics headFM;
	/**
	* The lines that compose this message's headline.
	*/
	Vector vHeadline = new Vector();
	/**
	* The font for the body text.
	*/
	private Font bodyFont;
	/**
	* The fontmetric for bodylines. Pre-calculated to save those previous
	* milliseconds.
	*/
	private FontMetrics bodyFM;
	/**
	* The lines that compose this message's body.
	*/
	Vector vBody = new Vector();

	/**
	* The constructor for a new message.
	* @param startat the initial position relative to the top of the applet
	* @param width the width of the viewable area within the applet
	* @param g the graphics context for the applet (to calculate fonts and
	* metrics)
	* @param fontHead the font to use for headlines
	* @param fontBody the font to use for body lines
	*/
	NewsMessage (int startat,int width,Graphics g,Font fontHead,Font fontBody)
	{
		// no height initially (duh!) and set the top to the starting position
		height = 0;
		top = startat;

		// hang on to all the other things we need later on
		headFont = fontHead;
		headFM = g.getFontMetrics(fontHead);
		bodyFont = fontBody;
		bodyFM = g.getFontMetrics(fontBody);
		maxWidth = width - 10;		// give it a touch of slack
	}

	/**
	* Change the position of this message. Since the applet is always scrolling
	* "up", the position is actually decremented towards 0.
	* @param amount the amount to change position
	*/
	protected void movePosition(int amount)
	{
		top -= amount;
	}
	/**
	* Reset the current top of this message to another value.
	* @param newtop the new position for the top of the message
	*/
	protected void resetTop(int newtop)
	{
		top = newtop;
	}
	/**
	* Get the current position of the bottom of this message.
	* @return the position of the bottom of the last line in the message in
	* pixels
	*/
	protected int getBottom()
	{
		return top + height;
	}
	/**
	* Get the current position of the top of this message.
	* @return the position of the top of the first headline in pixels
	*/
	protected int getTop()
	{
		return top;
	}
	/**
	* Set the link URL for this message. Takes either an absolute URL or a
	* relative one to the HTML page the applet is on. If it's invalid, it
	* remains unset.
	* @param docbase the URL of the HTML page of the applet
	* @param s the name of the URL to create
	*/
	protected void setLink(URL docbase,String s)
	{
		try
		{
			if (s.indexOf("http://") >= 0 )
				link = new URL(s);
			else
				link = new URL(docbase,s);
		}
		catch (MalformedURLException e)
		{
			link = null;
		}

	}
	/**
	* Get this message's URL if the given position is somewhere inside of it.
	* @param y the position to check against
	* @return the message's URL or null if the position is outside of it
	*/
	protected URL click(int y)
	{
		if (y >= top && y <= (top + height))
			return(link);
		return(null);
	}
	/**
	* Set the URL target for this message.
	* @param target the URL target
	*/
	protected void setTarget(String t)
	{
		target = new String(t);
	}
	/**
	* Get the URL target for this message.
	* @return the URL target
	*/
	protected String getTarget()
	{
		return target;
	}
	/**
	* Append a new headline to this message.
	* @param s the line to add
	*/
	protected void addHeadLine(String s)
	{
		addLine(vHeadline,s,headFM);
	}
	/**
	* Append a new body line to this message.
	* @param s the line to add
	*/
	protected void addMessageLine(String s)
	{
		addLine(vBody,s,bodyFM);
	}

	/**
	* Add a line to the message. Calculates line-wrapping so text fits within
	* the viewable area and updates the height (in pixels) of the message.
	* <P>
	* Words that are too long for a line by themselves are chopped. Blank lines
	* are also allowed.
	* @param v the portion of the message this line is to be appended to
	* (headlines or body lines)
	* @param line the line of text to add
	* @param fm the FontMetrics to use for this line
	*/
	private void addLine(Vector v,String line,FontMetrics fm)
	{
		// line heights
		int h = fm.getHeight();

		// add a blank line - fake it by putting a newline in
		if (line.length() == 0)
		{
			v.addElement("\n");
			height += h;
			return;
		}

		// If there's already text in there, grab the last line so we can wrap
		// it. Remove it so it can be re-appended, depening on the wrapping.
		String s;
		if (v.size() > 0)
		{
			s = (String)v.lastElement();
			if (s.equals("\n"))
				s = line;
			else
			{
				v.removeElement(s);
				height -= h;
				s += " " + line;
			}
		}
		// otherwise, it's the first line in this portion
		else
			s = line;

		// break our working copy apart on spaces
		String t = null;
		StringTokenizer toke = new StringTokenizer(s," ");
		while (toke.hasMoreTokens())
		{
			String last = t;
			String lastToken = toke.nextToken();

			if (t==null)
				t = lastToken;
			else
				t += " " + lastToken;

			// now it's too long
			if (fm.stringWidth(t) > maxWidth)
			{
				// eep! the last word itself was too long!
				// whittle it down to something that will fit
				if (last == null || t.indexOf(' ') < 0)
				{
					int x;
					for (x = 1; x < t.length(); x++)
					{
						String t2 = t.substring(0,x);
						if (fm.stringWidth(t2+"-") > maxWidth) break;
					}
					v.addElement(t.substring(0,x-1) + "-");
					t = t.substring(x);
				}
				// otherwise, just append the last known line
				else
				{
					v.addElement(last);
					t = lastToken;
				}
				height += h;
			}
		}

		// examine the last remnant
		// once again, too long for the line - we can assume it's one word
		// because of the nature of the loop above
		while (fm.stringWidth(t) > maxWidth)
		{
			int x;
			for (x = 1; x < t.length(); x++)
			{
				String t2 = t.substring(0,x);
				if (fm.stringWidth(t2+"-") > maxWidth) break;
			}
			v.addElement(t.substring(0,x-1) + "-");
			height += h;
			t = t.substring(x);
		}

		// add the last remains
		v.addElement(t);
		height += h;
	}

	/**
	* Draw the text of this message on the specified Graphics object with the
	* specified colors.
	* @param g the Graphics object to draw on -- usually an off-screen image
	* @param border the width of the border
	* @param headColor the color for headlines
	* @param msgColor the color for normal text
	* @param urlColor the color for linked text
	*/
	protected void draw (Graphics g,int border,
					Color headColor,Color msgColor,Color urlColor)
	{
		// is this thing even in the picture?
		if (top+height <= 0) return;

		// do the headlines
		g.setColor(headColor);
		g.setFont(headFont);
		int startat = drawline(g,vHeadline,border,headFM,top);
		// then do the message
		g.setColor(link==null ? msgColor : urlColor);
		g.setFont(bodyFont);
		drawline(g,vBody,border,bodyFM,startat);
	}
	/**
	* Draws the specified vector of lines onto a Graphics object using the
	* params.
	* @param g the Graphics object to draw on -- usually an off-screen image
	* @param v the Vector of text lines to draw
	* @param border the width of the border
	* @param fm the font metrics for the font
	* @param start the position to start drawing at
	* @return the ending position of this group of lines
	*/
	private int drawline(Graphics g,Vector v,int border,FontMetrics fm,int start)
	{
		int h = fm.getHeight();
		int y = fm.getAscent();

		for (Enumeration e = v.elements(); e.hasMoreElements(); ) {
			String s = (String)e.nextElement();
			if (s.equals("\n"))  s = " ";
			g.drawString(s,border+5,start+y);
			start += h;
		}
		return start;
	}
}


Index

How to Add Java Applets to Your Site

New on the Java Boutique:

New Review:

Time Management Made Easy with the Quartz Enterprise Job Scheduler
Why not just use the Java timer API? This open source scheduling API boasts simplicity, ease-of-integration, a well-rounded feature set, and it's free!

New Applet:

Reverse Complement
Reverse Complement is a simple applet that converts DNA or RNA sequences into three useful formats.

Elsewhere on internet.com:

WebDeveloper Java
Lots of Java information on webdeveloper.com

WDVL Java
Thorough Java resource at the Web Developer's Virtual Library.

ScriptSearch Java
Hundreds of free Java code files to download.

jGuru: Your View of the Java Universe
Customizable portal with online training, FAQs, regular news updates, and tutorials.