home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC Online 1997 October
/
PCO1097.ISO
/
FilesBBS
/
FREI
/
FSCROLL.EXE
/
SRC
/
FunScrollAnimate.java
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
Macintosh to JP
NeXTSTEP
RISC OS/Acorn
Shift JIS
UTF-8
Wrap
Java Source
|
1997-09-02
|
39.2 KB
|
1,540 lines
/*
* Copyright (c) 1995 by Jan Andersson, Torpa Konsult AB.
*
* Permission to use, copy, and distribute this software for
* NON-COMMERCIAL purposes and without fee is hereby granted
* provided that this copyright notice appears in all copies.
*/
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.MediaTracker;
import java.awt.Image;
import java.util.Vector;
import java.util.StringTokenizer;
/**
* FunScroll Animate Text(s) and Image(s)
*
* @version 1.3 97/09/01
* @author Jan Andersson (janne@torpa.se)
*/
public class FunScrollAnimate
{
// states:
static final int START = 0; // start sequence
static final int SHOW = 1; // show sequence
static final int END = 2; // end sequence
static final int DONE = 3; // done sequence
int state = START; // animate state
FunScroll appl; // FunScroll applet
FunScrollAttr attr; // attributes
String imageName; // name of image
Image image; // image
boolean imageLoaded = false; // true when image loaded
int imageWidth; // image width
int imageHeight; // image height
Image bgImage; // background image
String bgText; // background text
boolean bgImageLoaded = false; // true when background image loaded
int bgImageWidth; // background image width
int bgImageHeight; // background image height
MediaTracker mediaTracker = null; // media tracker
FunScrollFade startFadeTrans = null;
FunScrollFade endFadeTrans = null;
String unparsedText; // unparsed text line
String[] lines; // lines of text
protected int[] lineWidths; // how wide each line is
int noOfLines = 1; // number of lines
char chars[]; // the characters
int noOfChars; // number of characters
int xPos[]; // the x positions
int yPos[]; // the y positions
boolean visible[]; // flags set to true if character visible
int typedCount = 0; // used for "typed" characters
int currLineIndex = 0; // used for "line-up" lines
int linePos = 0; // used for "line-up" lines
int delayCount = 0; // used to delay for a while
int margin; // the margin
int offsetX; // the x offset
int offsetY; // the y offset
int width; // the applet width
int height; // the applet height
int scrollWidth; // scrolling area width
int scrollHeight; // scrolling area height
int textHeight; // text height
int lineHeight; // text line height
Color bg; // background color
Color fg; // foreground color
Color darkBg; // dark background
Color lightDarkBg; // lightdark background
Color brightBg; // bright background
Color brightFg; // bright foreground
Font font; // font
FontMetrics fontMetrics; // font metrics
int ascent; // font ascent
int descent; // font descent
int leading; // font leading
int maxWidth; // max width
int sinDegree; // used for sin-wave text
int xStart = -1; // starting X pos
int yStart = -1; // starting Y pos
int dx; // x distance to move
int dy; // y distance to move
public FunScrollAnimate(FunScroll appl, String line,
Font font, Color fg, Color bg,
int dx, int dy, String delim)
{
this.appl = appl;
this.font = font;
this.fg = fg;
this.bg = bg;
this.dx = dx;
this.dy = dy;
this.unparsedText = line;
// parse message line and init attributes
attr = new FunScrollAttr(line, delim);
appl.dbg("Parsed Attributes:");
if (attr.type() == attr.TEXT)
appl.dbg(" msg:" + attr.param());
else
appl.dbg(" image:" + attr.param());
appl.dbg(" startScroll:" + attr.startScroll());
appl.dbg(" endScroll:" + attr.endScroll());
appl.dbg(" showDelay:" + attr.showDelay());
appl.dbg(" endDelay:" + attr.endDelay());
appl.dbg(" style:" + attr.style());
appl.dbg(" drawStyle:" + attr.drawStyle());
appl.dbg(" color:" + attr.color());
appl.dbg(" bgImage:" + attr.bgImage());
appl.dbg(" bgText:" + attr.bgText());
appl.dbg(" bgOffsetX:" + attr.bgOffsetX());
appl.dbg(" bgOffsetY:" + attr.bgOffsetY());
appl.dbg(" offsetX:" + attr.offsetX());
appl.dbg(" offsetY:" + attr.offsetY());
appl.dbg("dy:" + dy + " dx:" + dx);
// get color attribute (if specified)
if (attr.color() != null)
this.fg = appl.readColor(attr.color(), fg);
appl.dbg(" color:" + fg);
// Init font stuff (we probably need this)
fontMetrics = appl.getFontMetrics(font);
ascent = fontMetrics.getAscent();
descent = fontMetrics.getDescent();
leading = fontMetrics.getLeading();
lineHeight = fontMetrics.getHeight();
if (attr.type() == attr.TEXT)
initText();
else
initImage();
}
/**
* Reset animation
*/
void reset()
{
int offsetX = attr.offsetX();
int offsetY = attr.offsetY();
int width = appl.size().width;
int height = appl.size().height;
int margin = appl.getMargin();
if (this.offsetX != offsetX ||
this.offsetY != offsetY ||
this.width != width ||
this.height != height) {
startFadeTrans = null;
endFadeTrans = null;
}
this.offsetX = offsetX;
this.offsetY = offsetY;
this.width = width;
this.height = height;
this.margin = margin;
this.scrollWidth = width - 2*margin - offsetX;
this.scrollHeight = height - 2*margin - offsetY;
if (startFadeTrans != null)
startFadeTrans.resetFrameIndex();
if (endFadeTrans != null)
endFadeTrans.resetFrameIndex();
if (attr.type() == attr.TEXT)
resetText();
else
resetImage();
// re-init background image
initBgImage();
}
/**
* Update. I.e move and paint.
*/
boolean update(Graphics g)
{
// reset font and foreground
g.setFont(font);
g.setColor(fg);
// Animate
if (attr.type() == attr.TEXT)
animateText();
else
animateImage();
if (state == DONE && delayCount <= 0)
return true; // we are done!
// Draw background image if defined
if (bgImage != null)
paintBgImage(g);
// set new clip rext if offsetX or offsetY specified
if (offsetX != 0 || offsetY != 0)
g.clipRect(margin + offsetX,
margin + offsetY,
width - (2*margin) - offsetX,
height- (2*margin) - offsetY);
// paint text
if (attr.type() == attr.TEXT)
paintText(g);
else
paintImage(g);
return false;
}
/**
* Init background image (if specified)
*/
void initBgImage()
{
if (bgImage != null)
return; // already initiated...
bgImage = null;
if (attr.bgImage() == null) {
// no background image specified; check for background text...
if (attr.bgText() != null) {
// background text specified; create bgImage
bgText = attr.bgText();
bgImage = imageCreateEmpty(getBgTextSize().width,
getBgTextSize().height);
}
}
else {
// get background image
bgImage = appl.getImage(appl.getCodeBase(), attr.bgImage());
}
if (bgImage != null) {
// we have a background image specified; start loading
imageLoad(bgImage, 0);
bgImageWidth = 0;
bgImageHeight = 0;
bgImageLoaded = false;
checkBgImage();
}
}
/**
* Check if image loaded
*/
boolean checkBgImage()
{
if (!imageCheck(0))
return false;
FunScroll.dbg("checkBgImage: loaded!");
bgImageLoaded = true;
bgImageWidth = bgImage.getWidth(null);
bgImageHeight = bgImage.getHeight(null);
if (bgText != null)
// write background text to background image
imageDrawString(bgImage, bgText, 0, 0);
return true; // loaded
}
Dimension getBgTextSize()
{
int bgTextWidth = fontMetrics.stringWidth(bgText);
int bgTextHeight = lineHeight;
return new Dimension(bgTextWidth, bgTextHeight);
}
void paintBgImage(Graphics g)
{
if (!bgImageLoaded)
checkBgImage();
if (bgImageLoaded) {
int x = (width-bgImageWidth)/2;
int y = (height-bgImageHeight)/2;
if (attr.bgOffsetX() != 0)
x = attr.bgOffsetX();
if (attr.bgOffsetY() != 0)
y = attr.bgOffsetY();
g.drawImage(bgImage, x, y, null);
}
}
// -----------------------------------------------------------------
// Text related functions
/**
* Init text attributes
*/
void initText()
{
// init lines of text
lines = getLines(attr.param());
noOfLines = lines.length;
// init line widths
lineWidths = getLineWidths(lines);
// cound number of characters
noOfChars = 0;
for (int i=0; i < noOfLines; i++)
noOfChars += lines[i].length();
// init character arrays
chars = new char[noOfChars];
xPos = new int[noOfChars];
yPos = new int[noOfChars];
visible = new boolean[noOfChars];
int charIndex = 0;
int currXPos = 0;
int currYPos = ascent;
textHeight = 0;
maxWidth = 0;
for (int i=0; i < noOfLines; i++) {
int lineLength = lines[i].length();
int lineStartIndex = charIndex;
for (int j=0; j<lineLength; j++) {
chars[charIndex] = lines[i].charAt(j);
xPos[charIndex] = (j==0) ? 0 :
fontMetrics.charsWidth(
chars, lineStartIndex, j);
yPos[charIndex] = currYPos;
charIndex++;
}
maxWidth = Math.max(maxWidth, xPos[charIndex-1]);
textHeight += lineHeight;
currYPos += lineHeight;
}
if (attr.style() == FunScrollAttr.NERVOUS ||
attr.style() == FunScrollAttr.SINEWAVE)
// need some extra space here!
textHeight += 4;
}
/**
* Parse text line(s)
*/
String[] parseLines(String msg)
{
Vector linesOftext = new Vector();
StringTokenizer st = new StringTokenizer(msg, "\\n\n", true);
boolean escape = false;
boolean newLine = false;
String line = new String();
String token = null;
while (st.hasMoreTokens()) {
token = st.nextToken();
if (token.equals("\\"))
// escaped characted; wait for next character
escape = true;
else if (token.equals("n") && escape) {
// got "\n" - line break
newLine = true;
escape = false;
}
else if (token.equals("\n"))
newLine = true;
else {
if (escape)
line += "\\";
line += token;
}
if (newLine) {
linesOftext.addElement(line);
line = new String();
token = null;
newLine = false;
}
}
if (line.length() > 0) {
linesOftext.addElement(line);
}
int noOfLines = linesOftext.size();
String[] lines = new String[noOfLines];
for (int i=0; i < noOfLines; i++)
lines[i] = (String)linesOftext.elementAt(i);
return lines;
}
/**
* Reset array of x positions
*/
void resetTextX()
{
int currXPos = 0;
int currYPos = ascent;
int charIndex = 0;
for (int i=0; i < noOfLines; i++) {
int lineLength = lines[i].length();
int lineStartIndex = charIndex;
for (int j=0; j<lineLength; j++) {
xPos[charIndex] = (j==0) ? 0 :
fontMetrics.charsWidth(
chars, lineStartIndex, j);
charIndex++;
}
}
}
/**
* Reset width and height
*/
void resetText()
{
int scroll = attr.startScroll();
switch (scroll) {
case FunScrollAttr.NONE:
case FunScrollAttr.FADE:
case FunScrollAttr.TYPED:
xStart = (scrollWidth-maxWidth)/2;
yStart = (scrollHeight-textHeight)/2;
break;
case FunScrollAttr.LEFT:
xStart = scrollWidth-dx-margin;
yStart = (scrollHeight-textHeight)/2;
break;
case FunScrollAttr.RIGHT:
xStart = dx+margin-maxWidth;
yStart = (scrollHeight-textHeight)/2;
break;
case FunScrollAttr.UP:
xStart = (scrollWidth-maxWidth)/2;
yStart = scrollHeight-descent-margin;
break;
case FunScrollAttr.UP_LINE:
xStart = (scrollWidth-maxWidth)/2;
yStart = scrollHeight-descent-margin;
break;
case FunScrollAttr.DOWN:
xStart = (scrollWidth-maxWidth)/2;
yStart = 0-textHeight+margin;
break;
}
// adjust for margin and offsets
xStart += offsetX;
yStart += offsetY;
linePos = yStart;
// adjust for margin
//width -= 2*margin;
//height -= 2*margin;
// Reset array of x positions
resetTextX();
// reset typed and characters and lines count
currLineIndex = 0;
typedCount = 0;
// reset state
state = START;
FunScroll.dbg("State: START");
}
public String getUnparsedTextLine()
{
return unparsedText;
}
/**
* Animate text
*/
void animateText()
{
boolean switchState = false;
int scroll = FunScrollAttr.NONE;
switch (state) {
case START:
// start sequence
scroll = attr.startScroll();
if (scroll == FunScrollAttr.NONE) {
// no animation; just switch state
switchState = true;
}
else if (scroll == FunScrollAttr.FADE) {
if (startFadeTrans != null && startFadeTrans.done()) {
// fade frames all displayed; switch state
switchState = true;
}
}
else {
// some other kind of animation; check if all characters displ.
if (textDisplayed(scroll)) {
// yupp; switch state
switchState = true;
if (scroll == FunScrollAttr.UP_LINE)
// reset yStart after UP_LINE animation
yStart = margin + offsetY + leading;
}
}
if (!switchState) {
// just move text (scroll)
moveText(scroll);
updateVisible(scroll);
break;
}
// switch state
updateVisible(scroll);
state = SHOW;
FunScroll.dbg("State: SHOW");
delayCount = attr.showDelay();
// fall trough!
case SHOW:
// show sequence
if (--delayCount >= 0) {
// delay. I.e break out
break;
}
// switch state
state = END;
FunScroll.dbg("State: END");
// fall trough!
case END:
// end sequence
if (attr.endScroll() == attr.FADE) {
if (endFadeTrans != null && endFadeTrans.done()) {
// fade frames all displayed
state = DONE;
FunScroll.dbg("State: DONE");
delayCount = attr.endDelay();
// fall trough!
}
else
break;
}
// check if all characters still visible
else if (updateVisible(attr.endScroll()) == 0 ||
attr.endScroll() == FunScrollAttr.NONE) {
// none visible or no end animation; switch state
state = DONE;
FunScroll.dbg("State: DONE");
delayCount = attr.endDelay();
return;
}
else {
moveText(attr.endScroll());
}
break;
case DONE:
// done sequence; just delay
delayCount--;
break;
}
}
/**
* Return true if (all) text is displayed
*/
boolean textDisplayed(int scroll)
{
switch (scroll) {
case FunScrollAttr.LEFT:
// scrolling left
if (maxWidth > scrollWidth) {
// text is wider that applet width
if (maxWidth+xStart < scrollWidth-4*dx)
return true;
}
else {
int appletMidPoint = scrollWidth/2;
int textMidPoint = xStart+maxWidth/2;
if (textMidPoint <= appletMidPoint)
return true;
}
break;
case FunScrollAttr.RIGHT:
// scrolling right
if (maxWidth > scrollWidth) {
// text is wider that applet width
if (xPos[0]+xStart > 4*dx)
return true;
}
else {
int appletMidPoint = scrollWidth/2;
int textMidPoint = xStart+maxWidth/2;
if (textMidPoint >= appletMidPoint)
return true;
}
break;
case FunScrollAttr.UP:
// scrolling up
if (yStart <= (scrollHeight-textHeight)/2 + margin)
return true;
break;
case FunScrollAttr.UP_LINE:
// scrolling up (line-by-line)
if (currLineIndex >= noOfLines)
return true;
if (currLineIndex == noOfLines-1) {
int stopLinePos = margin + offsetY + ascent + leading +
(lineHeight*currLineIndex);
if (linePos <= stopLinePos)
return true;
}
break;
case FunScrollAttr.DOWN:
// scrolling down
if (yStart >= (scrollHeight-textHeight)/2 + margin)
return true;
break;
case FunScrollAttr.TYPED:
// "typed" characters
if (typedCount > noOfChars)
return true;
break;
}
return false;
}
/**
* update array with flags if characters are visible. Return
* number of visible characters.
*/
int updateVisible(int scroll)
{
int visibleCount = 0;
boolean checkTypedCount = false;
if (state == START && scroll == attr.TYPED)
checkTypedCount = true;
boolean checkLineCount = false;
int maxVisibleIndex = 0;
if (state == START && scroll == attr.UP_LINE) {
checkLineCount = true;
for (int i = 0; i <= currLineIndex && i < noOfLines; i++)
maxVisibleIndex += lines[i].length();
}
for (int i = 0; i < noOfChars; i++) {
if (checkTypedCount)
visible[i] = (i <= typedCount);
else if (checkLineCount)
visible[i] = (i < maxVisibleIndex);
else
visible[i] = (xPos[i]+xStart > margin &&
xPos[i]+xStart < width-margin &&
yPos[i]+yStart+lineHeight > margin &&
yPos[i]+yStart-lineHeight < height-margin);
if (visible[i])
visibleCount++;
}
// special treatment of explode animation
if (scroll == FunScrollAttr.EXPLODE) {
// if only 4 or less chars visible (per line) consider this as done
if (visibleCount <= (noOfLines*4))
visibleCount = 0;
}
return visibleCount;
}
void moveText(int scroll)
{
switch (scroll) {
case FunScrollAttr.LEFT:
xStart -= dx;
break;
case FunScrollAttr.RIGHT:
xStart += dx;
break;
case FunScrollAttr.UP:
yStart -= dy;
break;
case FunScrollAttr.UP_LINE:
if (currLineIndex < noOfLines) {
int stopLinePos = margin + offsetY + ascent + leading +
(lineHeight*currLineIndex);
if (linePos <= stopLinePos) {
// next line
currLineIndex++;
//linePos = scrollHeight-descent-margin+offsetY;
//linePos = scrollHeight+margin+offsetY;
linePos = yStart;
}
else
// move current line
linePos -= dy;
}
break;
case FunScrollAttr.DOWN:
yStart += dy;
break;
case FunScrollAttr.TYPED:
typedCount++;
break;
case FunScrollAttr.EXPLODE:
moveExplodeText();
break;
}
}
/**
* Move exploding text
*/
void moveExplodeText() {
int visibleChars = updateVisible(attr.endScroll());
// noOfChars
int mid = noOfChars/2;
int maxDist = maxWidth/20;
double explodeFactor = 1.1;
if (visibleChars/noOfChars < 0.2)
explodeFactor = 2;
else if(visibleChars/noOfChars < 0.5)
explodeFactor = 1.5;
for (int i = mid-1; i >= 0; i--) {
// move to the left
int moveDist = (int) ((mid - i) * explodeFactor);
moveDist = Math.min(maxDist, moveDist);
xPos[i] -= moveDist;
}
for (int i = mid; i < noOfChars; i++) {
// move to the right
int moveDist = (int) ((i - mid + 1 ) * explodeFactor);
if (moveDist == 0)
moveDist = 1;
moveDist = Math.min(maxDist, moveDist);
xPos[i] += moveDist;
}
}
/**
* Paint characters
*/
void paintText(Graphics g)
{
if (state == END || state == DONE) {
if (state == END && attr.endScroll() == FunScrollAttr.EXPLODE) {
paintExplode(g);
return;
}
else if (attr.endScroll() == FunScrollAttr.FADE) {
if (endFadeTrans == null) {
endFadeTrans = new FunScrollFade(
appl, width + margin, height + margin);
endFadeTrans.initToImage(appl.getOffImage());
}
else {
endFadeTrans.drawFrame(g);
return;
}
}
}
if (state == START && attr.startScroll() == FunScrollAttr.FADE) {
if (startFadeTrans == null) {
startFadeTrans = new FunScrollFade(
appl, width + margin, height + margin);
startFadeTrans.initFromImage(appl.getOffImage());
// create image to transform to...
Image image = imageCreateEmpty(width + margin, height + margin);
Graphics gr = image.getGraphics();
Image fromImage = startFadeTrans.getFromImage();
gr.drawImage(fromImage, 0, 0, null);
gr.setFont(font);
gr.setColor(fg);
paintNormal(gr);
startFadeTrans.initToImage(image);
gr.dispose();
gr = null;
}
startFadeTrans.drawFrame(g);
return;
}
// set foreground color
g.setColor(fg);
switch (attr.style()) {
case FunScrollAttr.SINEWAVE:
paintSineWave(g);
break;
case FunScrollAttr.NERVOUS:
paintNervous(g);
break;
default:
paintNormal(g);
break;
}
if (state == END && attr.endScroll() == FunScrollAttr.FADE) {
if (endFadeTrans != null)
endFadeTrans.initFromImage(appl.getOffImage());
}
}
/**
* Paint "exploding" text line
*/
void paintExplode(Graphics g) {
for (int i = 0; i < noOfChars; i++) {
if (visible[i])
drawNormalChar(g, i);
}
}
/**
* Paint normal text line
*/
void paintNormal(Graphics g) {
switch (attr.drawStyle()) {
case attr.ENGRAVE:
case attr.EMBOSS:
// pain emboss or engrave line
paintEmbossEngrave(g);
break;
case attr.SHADOW:
// pain shadowed line
paintShadow(g);
break;
case attr.NONE:
// draw normal line(s)
for (int i=0; i < noOfLines; i++) {
drawAlignedString(g, i,
xStart, yStart + ascent + (lineHeight*i));
}
break;
}
}
/**
* Paint emboss/engrave text line
*/
void paintEmbossEngrave(Graphics g) {
// init colors (first time)
if (darkBg == null) {
darkBg = FunScroll.darker(bg, 0.5);
lightDarkBg = FunScroll.darker(bg, 0.5 - (0.5/2));
brightBg = FunScroll.brighter(bg, 0.5);
}
int drawStyle = attr.drawStyle();
Color upperLeftColor = (drawStyle == attr.ENGRAVE) ? darkBg : brightBg;
Color upperRightColor = (drawStyle == attr.ENGRAVE) ? brightBg : darkBg;
Color mainColor = (drawStyle == attr.ENGRAVE) ? lightDarkBg : bg;
int depth = 1; // hardkoded ;-(
for (int i=0; i < noOfLines; i++) {
drawAlignedString(g, i,
xStart, yStart + ascent + (lineHeight*i));
// upper left edge
g.setColor(upperLeftColor);
drawAlignedString(g, i,
xStart,
yStart + ascent + (lineHeight*i) - depth);
// lower right edge
g.setColor(upperRightColor);
drawAlignedString(g, i,
xStart + depth*2,
yStart + ascent + (lineHeight*i) + depth);
// main body of the character
g.setColor(mainColor);
drawAlignedString(g, i,
xStart + depth,
yStart + ascent + (lineHeight*i));
}
}
/**
* Paint emboss/engrave text line
*/
void paintShadow(Graphics g) {
int shadowOffset = 4;
if (brightFg == null)
brightFg = FunScroll.brighter(fg, 0.75);
for (int i=0; i < noOfLines; i++) {
g.setColor(brightFg);
drawAlignedString(g, i,
xStart + shadowOffset,
yStart + ascent + (lineHeight*i) + shadowOffset);
g.setColor(fg);
drawAlignedString(g, i,
xStart,
yStart + ascent + (lineHeight*i));
}
}
/**
* draw aligned string
*/
void drawAlignedString(Graphics g, int index, int x, int y)
{
switch(attr.align()) {
case attr.LEFT:
break;
case attr.RIGHT:
x = width - x - lineWidths[index];
break;
case attr.CENTER:
x = x + (maxWidth - lineWidths[index])/2;
break;
}
if (state == START && attr.startScroll() == attr.TYPED)
drawTypedString(g, index, x, y);
else if (state == START && attr.startScroll() == attr.UP_LINE)
drawLineByLine(g, index, x, y);
else
g.drawString(lines[index], x, y);
}
/**
* draw "typed" chars of line
*/
void drawTypedString(Graphics g, int index, int x, int y)
{
// calc. number of chars in prev. lines and current line
int noOfCharsPrev = 0;
for (int i=0; i<index; i++)
noOfCharsPrev += lines[i].length();
int noOfCharsCurr = lines[index].length();
if (noOfCharsPrev + noOfCharsCurr < typedCount) {
// just print line
g.drawString(lines[index], x, y);
}
else {
// there are chars in this line that should be "typed"
int typedChars = typedCount - noOfCharsPrev;
if (typedChars > 0) {
String currLine = lines[index].substring(0, typedChars);
g.drawString(currLine, x, y);
}
}
}
/**
* draw "line by line"
*/
void drawLineByLine(Graphics g, int index, int x, int y)
{
if (index < currLineIndex) {
// just print line
int lineY = margin + offsetY + ascent + leading +
(lineHeight*index);
g.drawString(lines[index], x, lineY);
}
else if (index == currLineIndex) {
// draw at line pos
g.drawString(lines[index], x, linePos);
}
}
/**
* Paint sine-wave text line
*/
void paintSineWave(Graphics g) {
int currYPos = (noOfChars > 0) ? yPos[0] : 0;
int degree = sinDegree;
for (int i = noOfChars-1; i >= 0; i--) {
if (currYPos != yPos[i]) {
// new line
currYPos = yPos[i];
degree = sinDegree;
}
if (visible[i]) {
// draw character
int sinHeight = lineHeight/3;
int y = (int) (Math.sin(degree*3.1414/180) * sinHeight);
drawChar(g, i, xPos[i]+xStart, yPos[i]+yStart+y);
}
degree -= 15;
if (degree <= 0)
degree = 360;
}
sinDegree -= 15;
if (sinDegree <= 0)
sinDegree = 360;
}
/**
* Paint nervous text line
*/
void paintNervous(Graphics g) {
for (int i = 0; i < noOfChars; i++) {
if (visible[i])
drawNervousChar(g, i);
}
}
/**
* Draw nervous character
*/
void drawNervousChar(Graphics g, int index)
{
int x = (int)(Math.random() * 2) + xPos[index];
int y = (int)(Math.random() * 4) + yPos[index];
drawChar(g, index, x+xStart, y+yStart);
}
/**
* Draw normal character
*/
void drawNormalChar(Graphics g, int index)
{
drawChar(g, index, xPos[index]+xStart, yPos[index]+yStart);
}
/**
* Draw character
*/
void drawChar(Graphics g, int index, int x, int y)
{
if (state == START && attr.startScroll() == attr.UP_LINE) {
// adjust y pos to handle movement of line by line
int startIndexOfCurrentLine = 0;
int endIndexOfCurrentLine = 0;
for (int i = 0; i <= currLineIndex && i < noOfLines; i++) {
endIndexOfCurrentLine += lines[i].length();
if (i < currLineIndex)
startIndexOfCurrentLine = endIndexOfCurrentLine;
}
if (index >= startIndexOfCurrentLine &&
index < endIndexOfCurrentLine) {
//y = (y - yStart) + linePos;
int yPos = (y - yStart);
y = yPos + (linePos - yPos);
}
}
int drawStyle = attr.drawStyle();
if (drawStyle == attr.NONE || drawStyle == attr.SHADOW) {
if (drawStyle == attr.SHADOW) {
int shadowOffset = 4;
if (brightFg == null)
brightFg = FunScroll.brighter(fg, 0.75);
g.setColor(brightFg);
g.drawChars(chars, index, 1,
x + shadowOffset, y + shadowOffset);
}
// default draw style
g.setColor(fg);
g.drawChars(chars, index, 1, x, y);
return;
}
// draw style is ENGRAVE or EMBOSS
// init colors (first time)
if (darkBg == null) {
darkBg = FunScroll.darker(bg, 0.5);
lightDarkBg = FunScroll.darker(bg, 0.5 - (0.5/2));
brightBg = FunScroll.brighter(bg, 0.5);
}
int depth = 1; // hardkoded ;-(
Color upperLeftColor =
(drawStyle == attr.ENGRAVE) ? darkBg : brightBg;
Color upperRightColor =
(drawStyle == attr.ENGRAVE) ? brightBg : darkBg;
Color mainColor =
(drawStyle == attr.ENGRAVE) ? lightDarkBg : bg;
// upper left edge
g.setColor(upperLeftColor);
g.drawChars(chars, index, 1, x, y-depth);
// lower right edge
g.setColor(upperRightColor);
g.drawChars(chars, index, 1, x+depth*2, y+depth);
// main body of the character
g.setColor(mainColor);
g.drawChars(chars, index, 1, x+depth, y);
}
// -----------------------------------------------------------------
// Image related functions
/**
* Init image attributes
*/
void initImage()
{
String imageName = attr.param();
FunScroll.dbg("get image:" + imageName);
image = appl.getImage(appl.getCodeBase(), imageName);
imageLoad(image, 1);
imageWidth = 0;
imageHeight = 0;
imageLoaded = false;
checkImage(); // to start loading
}
/**
* Check if image loaded
*/
boolean checkImage()
{
if (!imageCheck(1))
return false; //
FunScroll.dbg("checkImage: loaded!");
imageLoaded = true;
imageWidth = image.getWidth(null);
imageHeight = image.getHeight(null);
return true; // loaded
}
/**
* Reset width and height
*/
void resetImage()
{
if (!imageLoaded)
return;
int scroll = attr.startScroll();
switch (scroll) {
case FunScrollAttr.NONE:
case FunScrollAttr.FADE:
xStart = (scrollWidth-imageWidth)/2;
yStart = (scrollHeight-imageHeight)/2;
break;
case FunScrollAttr.LEFT:
xStart = scrollWidth-dx-margin;
yStart = (scrollHeight-imageHeight)/2;
break;
case FunScrollAttr.RIGHT:
xStart = dx+margin-imageWidth;
yStart = (scrollHeight-imageHeight)/2;
break;
case FunScrollAttr.UP:
xStart = (scrollWidth-imageWidth)/2;
yStart = scrollHeight-margin;
break;
case FunScrollAttr.DOWN:
xStart = (scrollWidth-imageWidth)/2;
yStart = 0-imageHeight+margin;
break;
}
// adjust for margin and offsets
xStart += offsetX;
yStart += offsetY;
// adjust for margin
//width -= 2*margin;
//height -= 2*margin;
// reset state
state = START;
FunScroll.dbg("State: START");
}
void animateImage()
{
if (!imageLoaded) {
if (checkImage())
resetImage();
else {
state = DONE;
return;
}
}
boolean switchState = false;
int scroll = FunScrollAttr.NONE;
switch (state) {
case START:
// start sequence
scroll = attr.startScroll();
if (scroll == FunScrollAttr.NONE) {
// no animation; just switch state
switchState = true;
}
else if (scroll == FunScrollAttr.FADE) {
if (startFadeTrans != null && startFadeTrans.done()) {
// fade frames all displayed; switch state
switchState = true;
}
}
else {
// some kind of animation; check if image displ.
if (imageDisplayed(scroll)) {
// yupp; switch state
switchState = true;
}
}
if (!switchState) {
// just move image (scroll)
moveImage(scroll);
break;
}
// switch state
state = SHOW;
FunScroll.dbg("State: SHOW");
delayCount = attr.showDelay();
// fall trough!
case SHOW:
// show sequence
if (--delayCount >= 0) {
// delay. I.e break out
break;
}
// switch state
state = END;
FunScroll.dbg("State: END");
// fall trough!
case END:
// end sequence
if (attr.endScroll() == attr.FADE) {
if (endFadeTrans != null && endFadeTrans.done()) {
// fade frames all displayed
state = DONE;
FunScroll.dbg("State: DONE");
delayCount = attr.endDelay();
// fall trough!
}
else
break;
}
// check if image still visible
else if (!imageVisible(attr.endScroll()) ||
attr.endScroll() == FunScrollAttr.NONE) {
// none visible or no end animation; switch state
state = DONE;
FunScroll.dbg("State: DONE");
delayCount = attr.endDelay();
return;
}
else {
moveImage(attr.endScroll());
}
break;
case DONE:
// done sequence; just delay
delayCount--;
break;
}
}
void moveImage(int scroll)
{
switch (scroll) {
case FunScrollAttr.LEFT:
xStart -= dx;
break;
case FunScrollAttr.RIGHT:
xStart += dx;
break;
case FunScrollAttr.UP:
yStart -= dy;
break;
case FunScrollAttr.DOWN:
yStart += dy;
break;
}
}
boolean imageDisplayed(int scroll)
{
switch (scroll) {
case FunScrollAttr.LEFT:
// scrolling left
if (imageWidth > width) {
// image is wider that applet width
if (imageWidth+xStart < width-4*dx)
return true;
}
else {
int appletMidPoint = width/2;
int imageMidPoint = xStart+imageWidth/2;
if (imageMidPoint <= appletMidPoint)
return true;
}
break;
case FunScrollAttr.RIGHT:
// scrolling right
if (imageWidth > width) {
// image is wider that applet width
if (xPos[0]+xStart > 4*dx)
return true;
}
else {
int appletMidPoint = width/2;
int imageMidPoint = xStart+imageWidth/2;
if (imageMidPoint >= appletMidPoint)
return true;
}
break;
case FunScrollAttr.UP:
// scrolling up
if (yStart <= (height-imageHeight)/2-descent)
return true;
break;
case FunScrollAttr.DOWN:
// scrolling down
if (yStart >= (height-imageHeight)/2-descent)
return true;
break;
}
return false;
}
boolean imageVisible(int scroll)
{
boolean visible = (xStart+imageWidth > margin &&
xStart < width-margin &&
yStart+imageHeight > margin &&
yStart < height-margin);
return visible;
}
void paintImage(Graphics g)
{
if (imageWidth == 0)
return;
int x = xStart;
int y = yStart;
if ((state == END || state == DONE) &&
attr.endScroll() == FunScrollAttr.FADE) {
if (endFadeTrans == null) {
endFadeTrans = new FunScrollFade(
appl, width + margin, height + margin);
endFadeTrans.initToImage(appl.getOffImage());
}
else {
endFadeTrans.drawFrame(g);
return;
}
}
if (state == START && attr.startScroll() == FunScrollAttr.FADE) {
if (startFadeTrans == null) {
startFadeTrans = new FunScrollFade(
appl, width + margin, height + margin);
startFadeTrans.initFromImage(appl.getOffImage());
// create image to transform to...
Image offImage = imageCreateEmpty(width + margin, height + margin);
Graphics gr = offImage.getGraphics();
Image fromImage = startFadeTrans.getFromImage();
gr.drawImage(fromImage, 0, 0, null);
gr.drawImage(image, x, y, null);
startFadeTrans.initToImage(offImage);
gr.dispose();
gr = null;
}
startFadeTrans.drawFrame(g);
return;
}
switch (attr.style()) {
case FunScrollAttr.SINEWAVE:
sinDegree -= 15;
if (sinDegree <= 0)
sinDegree = 360;
y += (int) (Math.sin(sinDegree*3.1414/180) * imageHeight/4);
break;
case FunScrollAttr.NERVOUS:
x = (int)(Math.random() * 2) + xStart;
y = (int)(Math.random() * 4) + yStart;
break;
default:
break;
}
g.drawImage(image, x, y, null);
if (state == END && attr.endScroll() == FunScrollAttr.FADE) {
if (endFadeTrans != null)
endFadeTrans.initFromImage(appl.getOffImage());
}
}
// -----------------------------------------------------------------
// Various Image support methods
/**
* Start loading image with spec. id
*/
void imageLoad(Image image, int id)
{
if (mediaTracker == null)
mediaTracker = new MediaTracker(appl);
mediaTracker.addImage(image, id);
}
/**
* Check if image with spec. id is loaded
*/
boolean imageCheck(int id)
{
if (!mediaTracker.checkID(id, true)) {
FunScroll.dbg("imageCheck: loading...");
return false; // still loading
}
if (mediaTracker.isErrorID(id)) {
// error during load
// BUG ALERT: we should display an error message!
FunScroll.dbg("imageCheck: error...");
return false;
}
return true;
}
/**
* Create empty image
*/
Image imageCreateEmpty(int width, int height)
{
return appl.createImage(width, height);
}
/**
* Draw string to image
*/
void imageDrawString(Image image, String text, int x, int y)
{
Graphics g = bgImage.getGraphics();
g.setColor(bg);
imageWidth = image.getWidth(null);
imageHeight = image.getHeight(null);
g.fillRect(0, 0, imageWidth, imageHeight);
g.setColor(fg);
g.setFont(font);
// BUG ALERT: should use the below to handle several lines
g.drawString(text, x, y+ascent);
g.dispose();
}
// -----------------------------------------------------------------
// Various methods to handle text string separated into lines
/**
* Get lines, by splitting a a string into (/n separated) lines
*/
String[] getLines(String text)
{
Vector linesOftext = new Vector();
StringTokenizer st = new StringTokenizer(text, "\\n\n", true);
boolean escape = false;
boolean newLine = false;
String line = new String();
String token = null;
while (st.hasMoreTokens()) {
token = st.nextToken();
if (token.equals("\\"))
// escaped characted; wait for next character
escape = true;
else if (token.equals("n") && escape) {
// got "\n" - line break
newLine = true;
escape = false;
}
else if (token.equals("\n"))
newLine = true;
else {
if (escape)
line += "\\";
line += token;
}
if (newLine) {
linesOftext.addElement(line);
line = new String();
token = null;
newLine = false;
}
}
if (line.length() > 0) {
linesOftext.addElement(line);
}
int noOfLines = linesOftext.size();
String[] lines = new String[noOfLines];
for (int i=0; i < noOfLines; i++)
lines[i] = (String)linesOftext.elementAt(i);
return lines;
}
/**
* Get line widths (array of int) for each line
*/
int[] getLineWidths(String[] lines)
{
int noOfLines = lines.length;
int[] lineWidths = new int[noOfLines];
for (int i=0; i < noOfLines; i++) {
lineWidths[i] = fontMetrics.stringWidth(lines[i]);
}
return lineWidths;
}
}